#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <sys/stat.h>
#include <time.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>

#include "interface.h"
#include "support.h"
#include "search.h"

const gchar STRING_NOT_APPLICABLE[] = "N/A";
const gchar STR_NO_CONTEXT_SEARCH_DONE[] = "\nNo context searching performed on this file. Make sure that the \"Containing text\" is enabled before searching.\n";

/* executable default list for all OSs */
const gchar *browserList[] = { "firefox",
                               "iexplore",
                               "opera",
                               NULL};
const gchar *texteditorList[] = { "gedit",
                                  "emacs",
                                  "notepad",
                                  NULL};
const gchar *fileexplorerList[] = { "nautilus",
                                    "iexplore",
                                    NULL};
const gchar **exeList[] = {browserList,
                           texteditorList,
                           fileexplorerList,
                           NULL};


/*************************************************************
 *  Search library functions
 *************************************************************/

void *walkDirectories(void *args)
{
  GObject *object = args; /* Get GObject pointer from args */
  searchControl *mSearchControl; /* Master status bar */
  searchData *mSearchData; /* Master search data */
  statusbarData *status; /* Master status bar */
  glong matchCount;
  GtkWidget *widget;

  gdk_threads_enter ();
  mSearchData = g_object_get_data(object, MASTER_SEARCH_DATA);
  mSearchControl = g_object_get_data(object, MASTER_SEARCH_CONTROL);
  status = g_object_get_data(object, MASTER_STATUSBAR_DATA);
  gdk_threads_leave ();

  g_assert(mSearchData != NULL);
  g_assert(mSearchControl != NULL);
  g_assert(status != NULL);

  widget = GTK_WIDGET(mSearchControl->goButton);

  /* Disable the Go button */
  gdk_threads_enter ();
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton), FALSE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton), TRUE);  
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton1), FALSE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton1), TRUE);  
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton2), FALSE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton2), TRUE);  
  gtk_widget_set_sensitive(lookup_widget(widget, "saveResults"), FALSE);
  gtk_widget_set_sensitive(lookup_widget(widget, "save_results1"), FALSE);
//  gtk_widget_set_sensitive(lookup_widget(widget, "printResults"), FALSE);
  gdk_threads_leave ();
  
  /* Disable go button, enable stop button.. */

  matchCount = phaseOneSearch(mSearchControl, mSearchData, status);
  
  if ((mSearchControl->cancelSearch == FALSE) && 
      ((mSearchControl->flags & SEARCH_TEXT_CONTEXT) != 0)) {
    matchCount = phaseTwoSearch(mSearchControl, mSearchData, status); 
  }

/* Re-enable the go button */
  gdk_threads_enter ();
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton), TRUE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton), FALSE);  
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton1), TRUE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton1), FALSE);  
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->goButton2), TRUE);
  gtk_widget_set_sensitive(GTK_WIDGET(mSearchControl->stopButton2), FALSE);  
  gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->goButton));
  if (matchCount > 0) {
      gtk_widget_set_sensitive(lookup_widget(widget, "saveResults"), TRUE);
      gtk_widget_set_sensitive(lookup_widget(widget, "save_results1"), TRUE);
//      gtk_widget_set_sensitive(lookup_widget(widget, "printResults"), TRUE);
  }
  gdk_threads_leave ();
  return;
  
}

glong phaseOneSearch(searchControl *mSearchControl, searchData *mSearchData, statusbarData *status)
{
  
  GPtrArray *scanDirStack; /* Pointer to the current open directory lister */
  GPtrArray *curDirStack; /* Pointer to the current folder base name (as a stack) */
  gchar *tmpFileName; /* Pointer to the current file name or sub-directory name returned by scanDirStack */
  gchar *tmpFullFileName; /* Pointer to the full filename or directory provided by */
  regex_t searchRegEx;
  gchar *tString;
  GtkTreeModel *sortedModel;
  glong matchCount = 0;
  GtkTextBuffer *buffer;
  
  /* Clear the tree view, and text view objects */
  gdk_threads_enter ();
  sortedModel = gtk_tree_view_get_model(GTK_TREE_VIEW (mSearchControl->listView));
  mSearchData->store = GTK_LIST_STORE(gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(sortedModel)));
  gtk_list_store_clear(mSearchData->store);
  buffer = gtk_text_view_get_buffer (mSearchControl->textView);
  gtk_text_buffer_set_text(buffer, "", -1); /* Clear text! */
  gdk_threads_leave ();

  /* Compile the regular expression */
  regcomp(&searchRegEx, mSearchControl->fileSearchRegEx, mSearchControl->fileSearchFlags);
    
  /* Initialise stacks */
  curDirStack = g_ptr_array_new();
  scanDirStack = g_ptr_array_new(); 
 
  /* Copy base directory out */
  tmpFullFileName = g_string_chunk_insert_const(mSearchData->locationChunk, mSearchControl->startingFolder);
  //  g_ptr_array_add(mSearchData->pLocationArray, tmpFullFileName); /* Store name in locations for future reference */
  g_ptr_array_add(curDirStack, tmpFullFileName);
  g_ptr_array_add(scanDirStack, g_dir_open(tmpFullFileName, 0, NULL));

  g_snprintf(status->constantString, MAX_FILENAME_STRING, "Phase 1 searching %s", tmpFullFileName);
  gdk_threads_enter ();
  gtk_statusbar_push(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status),
                     g_string_chunk_insert(status->statusbarChunk, status->constantString));
  gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
  gdk_threads_leave ();

  while ((curDirStack->len > 0) && (mSearchControl->cancelSearch == FALSE)) {
    
    /* Get next file from the current scan directory */
    tmpFileName = (char *)g_dir_read_name(GET_LAST_PTR(scanDirStack));
    if (tmpFileName == NULL) {
      
      gdk_threads_enter ();
      gtk_statusbar_pop(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status));
      gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
      gdk_threads_leave ();
      g_dir_close(DEL_LAST_PTR(scanDirStack));
      DEL_LAST_PTR(curDirStack);
      continue;
    }

    /* Check for hidden files (unless overriden by user) */
    if ((mSearchControl->flags & SEARCH_HIDDEN_FILES) == 0) {
      if (*tmpFileName == '.') {
        continue;
      }
    }
    
    /* Check if file name is actually a folder name */
    tmpFullFileName = g_strconcat(GET_LAST_PTR(curDirStack), "/", tmpFileName, NULL);
    if (g_file_test(tmpFullFileName, G_FILE_TEST_IS_SYMLINK)) {
      g_free(tmpFullFileName);
    } else {
      if (g_file_test(tmpFullFileName, G_FILE_TEST_IS_DIR)) {
        if ((mSearchControl->flags & SEARCH_SUB_DIRECTORIES) != 0) {
          g_snprintf(status->constantString, MAX_FILENAME_STRING, "Phase 1 searching %s", tmpFullFileName);
          gdk_threads_enter ();
          gtk_statusbar_push(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status),
                             g_string_chunk_insert(status->statusbarChunk, status->constantString));
          gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
          gdk_threads_leave ();
          tString = g_string_chunk_insert_const(mSearchData->locationChunk, tmpFullFileName);
          g_free(tmpFullFileName);
          g_ptr_array_add(curDirStack, tString);
          g_ptr_array_add(scanDirStack, g_dir_open(tString, 0, NULL));
          if (GET_LAST_PTR(scanDirStack) == NULL) {
            g_print("Error! Cannot read sub-directory");
          }
        }
      } else { /* Otherwise, a filename has been found */
        if ((((mSearchControl->flags & SEARCH_INVERT_FILES) == 0) && (regexec(&searchRegEx, tmpFileName, 0, NULL, 0) == 0)) ||
            (((mSearchControl->flags & SEARCH_INVERT_FILES) != 0) && (regexec(&searchRegEx, tmpFileName, 0, NULL, 0) != 0))) {
          if (statMatchPhase(tmpFullFileName, mSearchControl)) {
            g_ptr_array_add(mSearchData->fullNameArray, g_strdup(tmpFullFileName));
            g_ptr_array_add(mSearchData->fileNameArray, g_strdup(tmpFileName));
            g_ptr_array_add(mSearchData->pLocationArray, GET_LAST_PTR(curDirStack));
            if ((mSearchControl->flags & SEARCH_TEXT_CONTEXT) == 0) {
              matchCount ++;
              displayQuickMatch(mSearchControl, mSearchData);
            }
          }
        } else {
          g_free(tmpFullFileName); /* delete file as unnecessary */
        }
      }
    }
  }

  if ((mSearchControl->flags & SEARCH_TEXT_CONTEXT) == 0) {
    updateStatusFilesFound(matchCount, status, mSearchControl);
  }
  

  /* Clean up memory now! */
  g_ptr_array_free(curDirStack, FALSE);
  g_ptr_array_free(scanDirStack, FALSE);
  regfree(&searchRegEx);

  return matchCount;
}

glong phaseTwoSearch(searchControl *mSearchControl, searchData *mSearchData, statusbarData *status)
{
  glong matchCount=0;
  gint i;
  gsize length;
  gchar *tmpFileName, *contents;
  regex_t search;
  textMatch *newMatch;

  /* Update the status bar */
  g_snprintf(status->constantString, MAX_FILENAME_STRING, "Phase 2 starting...");
  gdk_threads_enter ();
  gtk_statusbar_pop(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status));
  gtk_statusbar_push(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status),
                     status->constantString);
  gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
  gdk_threads_leave ();

  /* Compile the regular expression */
  regcomp(&search, mSearchControl->textSearchRegEx, mSearchControl->textSearchFlags);
  
  /* Loop through all files, and get all of the matches from each */
  for (i=0; i<(mSearchData->fullNameArray->len); i++) {

    tmpFileName = g_ptr_array_index(mSearchData->fullNameArray, i);

  /* Update the status bar */
    gdk_threads_enter ();
    g_snprintf(status->constantString, MAX_FILENAME_STRING, "Phase 2 searching %s", tmpFileName);
    gtk_statusbar_pop(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status));
    gtk_statusbar_push(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status),
                       status->constantString);
    gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
    gdk_threads_leave ();
    
    /* Open file (if valid) */
    if (g_file_get_contents2(tmpFileName, &contents, &length, NULL)) {

      /* Try to get a match */
      if (getAllMatches(mSearchData, contents, length, &search)) {
        
        /* get full filename pointer */
        newMatch = GET_LAST_PTR(mSearchData->textMatchArray);
        newMatch->pFullName = g_ptr_array_index(mSearchData->fullNameArray, i);
        newMatch->pFileName = g_ptr_array_index(mSearchData->fileNameArray, i);
        newMatch->pLocation = g_ptr_array_index(mSearchData->pLocationArray, i);
        newMatch->fileSize = length;
        getLength(mSearchData, newMatch);      
        getFileType(mSearchData, newMatch);
        getModified(mSearchData, newMatch);
        
        /* Convert the absolutes to relatives */
        dereferenceAbsolutes(mSearchData, contents, length);

        /* Display the matched string */
        displayMatch(mSearchControl, mSearchData); // , length, index); //pPhaseTwo, phaseOne);
        
        /* Increment the match counter */
        matchCount++;
      }
      g_free(contents); /* Clear file contents from memory */
      if (mSearchControl->cancelSearch == TRUE) {
        break;
      }
    }
  }

  /* Update statusbar with new data */
  updateStatusFilesFound(matchCount, status, mSearchControl);
    
  regfree(&search);
  return matchCount;
}

gboolean getAllMatches(searchData *mSearchData, gchar *contents, gsize length, regex_t *search /*, gint index*/ )
{
  regmatch_t subMatches[MAX_SUB_MATCHES];
  gint tmpConOffset = 0;
  gchar *tmpCon;
  lineMatch *newLineMatch = NULL;
  textMatch *newTextMatch = NULL;
  
  tmpCon = contents;

  /* Loop through getting all of the absolute match positions */
  while (regexec(search, tmpCon, MAX_SUB_MATCHES, subMatches, 0) == 0) {
    if ((subMatches[0].rm_so == 0) && (subMatches[0].rm_eo == 0)) {
      break;
    }
    
    if (newTextMatch == NULL) {
      newTextMatch = g_malloc(sizeof(textMatch));
      newTextMatch->matchIndex = mSearchData->lineMatchArray->len; /* pre-empt new line being added */
      newTextMatch->matchCount = 0;
      g_ptr_array_add(mSearchData->textMatchArray, newTextMatch);
    }
    newLineMatch = g_malloc(sizeof(lineMatch));
    newLineMatch->pLine = NULL;
    newLineMatch->lineCount = 0;
    newLineMatch->lineLen = -1;
    newLineMatch->lineNum = -1;
    newLineMatch->offsetStart = tmpConOffset + subMatches[0].rm_so;
    newLineMatch->offsetEnd = tmpConOffset + subMatches[0].rm_eo;
    newLineMatch->invMatchIndex = (mSearchData->textMatchArray->len - 1); /* create reverse pointer */
    g_ptr_array_add(mSearchData->lineMatchArray, newLineMatch);
    (newTextMatch->matchCount) ++;
    
    tmpCon += subMatches[0].rm_eo;
    tmpConOffset += subMatches[0].rm_eo;

    if (tmpConOffset >= length) {
      break;
    }
  }

  if (newTextMatch == NULL) {
    return FALSE;
  }
  return TRUE;
  
}

void dereferenceAbsolutes(searchData *mSearchData, gchar *contents, gsize length)
{
  gsize lineCount = 0;
  gchar *tmpCon = contents;
  gchar *lineStartPtr = tmpCon; /* Initialise it.. */
  gsize currentOffset = 0;
  gsize lineOffset = 1; /* Initialise at the starting char */
  gsize currentMatch = 0;
  gsize lineNumber = 0;
  gsize lineStart = 0;
  gchar *tmpString2; //, tmpString[MAX_STRING_SIZE];
  gboolean needLineEndNumber = FALSE;
  textMatch *textMatch = GET_LAST_PTR(mSearchData->textMatchArray);
  lineMatch *prevLineMatch, *newLineMatch = g_ptr_array_index(mSearchData->lineMatchArray,
                                              textMatch->matchIndex);
  
  gsize absMatchStart = newLineMatch->offsetStart;
  gsize absMatchEnd = newLineMatch->offsetEnd;

  /* Loop through whole file contents, one char at a time */
  while (currentOffset < length) { 

    /* Detect match start offset found - record localised stats */
    if (currentOffset == absMatchStart) {
      newLineMatch->offsetStart = (lineOffset - 1);
      newLineMatch->lineNum = (lineCount + 1);
    }
    
    /* Detect match end offset found - record localised stats */
    if (currentOffset == absMatchEnd) {
      newLineMatch->offsetEnd = (lineOffset - 1);
      newLineMatch->lineCount = ((lineCount - newLineMatch->lineNum) + 2);
      needLineEndNumber = TRUE;
    }
    
    if ((*tmpCon == '\n') || (currentOffset >= (length - 1))) {
      if (needLineEndNumber) {
        newLineMatch->lineLen = lineOffset;
        if (lineStartPtr == NULL) {
          g_print ("%s: Error line %d, %d:%d", GET_LAST_PTR(mSearchData->fullNameArray),
                   lineCount, newLineMatch->offsetStart, newLineMatch->offsetStart);
        }

	/* Copy line text (making sure that there is always enough space for extra long lines) */
        //        if ((newLineMatch->lineLen) > MAX_STRING_SIZE) {
        
        tmpString2 = g_strndup (lineStartPtr, (newLineMatch->lineLen - 1));
        newLineMatch->pLine = g_string_chunk_insert(mSearchData->textMatchChunk, tmpString2);
        g_free(tmpString2);

        prevLineMatch = newLineMatch;

        if (++currentMatch >= textMatch->matchCount) {
          break; /* All matches are actioned - done! */
        }
        newLineMatch = g_ptr_array_index(mSearchData->lineMatchArray,
                                         (textMatch->matchIndex + currentMatch)); /* Move pointer on one! */
        absMatchStart = newLineMatch->offsetStart;
        absMatchEnd = newLineMatch->offsetEnd;
        
        /* If next match is on that same line -- rewind the pointers */
        if (absMatchStart <= currentOffset) {
          currentOffset -= prevLineMatch->lineLen;
          tmpCon -= prevLineMatch->lineLen;          
          lineCount -= prevLineMatch->lineCount;
        }
        

        needLineEndNumber = FALSE;
            
      }
      lineCount ++;
      lineOffset = 0;
      lineStart = currentOffset;
      lineStartPtr = (tmpCon + 1); /* First charactor after the newline */
    }
    
    tmpCon ++;
    lineOffset++;
    currentOffset ++;
  }
  return;
}

void getLength(searchData *mSearchData, textMatch *newMatch)
{
  gchar *tmpString;

  if (newMatch->fileSize < 1024) {
    g_sprintf(tmpString, "%d bytes", newMatch->fileSize );
  } else if (newMatch->fileSize < (1024 * 1024)) {
    tmpString = g_strdup_printf ("%1.1f KB", ((float)newMatch->fileSize / 1024));
  } else {
    tmpString = g_strdup_printf ("%1.1f MB", ((float)newMatch->fileSize / 1024));
  }
  newMatch->pFileSize = (g_string_chunk_insert_const(mSearchData->fileSizeChunk, tmpString));
  g_free(tmpString);
}

void getFileType(searchData *mSearchData, textMatch *newMatch)
{
  textMatch *textMatch = GET_LAST_PTR(mSearchData->textMatchArray);
  gchar *tmpChar = textMatch->pFileName;
  gchar *tmpString;
  
  /* Find end of string */
  while (*tmpChar != '\0') {
    tmpChar ++;
  }
  
  /* Find string extension */
  tmpString[0] = '\0';
  while (tmpChar > textMatch->pFileName) {
    tmpChar --;
    if (*tmpChar == '.') { 
      tmpChar++;
      tmpString = g_strdup_printf ("%s-type", tmpChar);
      break;
    }
  }
  newMatch->pFileType = (g_string_chunk_insert_const(mSearchData->fileTypeChunk, tmpString));
  g_free(tmpString);
}

void getModified(searchData *mSearchData, textMatch *newMatch)
{
  struct stat buf;
  gint stringSize;
  gchar *tmpString;
  textMatch *textMatch = GET_LAST_PTR(mSearchData->textMatchArray);

  g_lstat(textMatch->pFullName, &buf);
  tmpString = g_strdup_printf ("%s", asctime (localtime ( &(buf.st_mtime) )));
  stringSize = strlen(tmpString);
  if (tmpString[stringSize - 1] == '\n') {
    tmpString[stringSize - 1] = '\0';
  }
  
  newMatch->mDate = buf.st_mtime;
  newMatch->pMDate = (g_string_chunk_insert_const(mSearchData->mDateChunk, tmpString));
  g_free(tmpString);
}

void getFileSize(searchData *mSearchData, textMatch *newMatch)
{
  struct stat buf;

  g_lstat(newMatch->pFullName, &buf);
  newMatch->fileSize = buf.st_size;
}

void displayMatch(searchControl *mSearchControl, searchData *mSearchData)
{
  textMatch *newMatch  = GET_LAST_PTR(mSearchData->textMatchArray);
  
  gdk_threads_enter ();
  gtk_list_store_append (mSearchData->store, mSearchData->iter);
  gtk_list_store_set (mSearchData->store, mSearchData->iter,
                      FULL_FILENAME_COLUMN, newMatch->pFullName,
                      FILENAME_COLUMN, newMatch->pFileName,
                      LOCATION_COLUMN, newMatch->pLocation,
                      SIZE_COLUMN, newMatch->pFileSize,
                      INT_SIZE_COLUMN, newMatch->fileSize,
                      MATCHES_COUNT_COLUMN, newMatch->matchCount,
                      MATCH_INDEX_COLUMN, newMatch->matchIndex,
                      MODIFIED_COLUMN, newMatch->pMDate,
                      INT_MODIFIED_COLUMN, newMatch->mDate,
                      TYPE_COLUMN, newMatch->pFileType,
                      MATCHES_COUNT_STRING_COLUMN, g_strdup_printf ("%d", newMatch->matchCount),
                      -1);
  gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->listView));    
  gdk_threads_leave ();
  return;
}

void displayQuickMatch(searchControl *mSearchControl, searchData *mSearchData)
{
  textMatch *newMatch = g_malloc(sizeof(textMatch));
  g_ptr_array_add(mSearchData->textMatchArray, newMatch);
  newMatch->pFullName = GET_LAST_PTR(mSearchData->fullNameArray);
  newMatch->pFileName = GET_LAST_PTR(mSearchData->fileNameArray);
  newMatch->pLocation = GET_LAST_PTR(mSearchData->pLocationArray);
  getModified(mSearchData, newMatch);
  getFileType(mSearchData, newMatch);
  getFileSize(mSearchData, newMatch);
  getLength(mSearchData, newMatch);
  
  gdk_threads_enter ();
  gtk_list_store_append (mSearchData->store, mSearchData->iter);
  gtk_list_store_set (mSearchData->store, mSearchData->iter,
                      FULL_FILENAME_COLUMN, newMatch->pFullName,
                      FILENAME_COLUMN, newMatch->pFileName,
                      LOCATION_COLUMN, newMatch->pLocation,
                      SIZE_COLUMN, newMatch->pFileSize,
                      INT_SIZE_COLUMN, newMatch->fileSize,
                      MODIFIED_COLUMN, newMatch->pMDate,
                      INT_MODIFIED_COLUMN, newMatch->mDate,
                      TYPE_COLUMN, newMatch->pFileType,
                      MATCHES_COUNT_STRING_COLUMN, STRING_NOT_APPLICABLE,
                      -1);
  gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->listView));    
  gdk_threads_leave ();
  return;
}

void updateStatusFilesFound(const gsize matchCount, statusbarData *status, searchControl *mSearchControl)
{
    /* Update statusbar with new data */
    gdk_threads_enter();
    if (matchCount == 1) {
        g_snprintf(status->constantString, MAX_FILENAME_STRING, "%d file found", matchCount);
    } else {
        g_snprintf(status->constantString, MAX_FILENAME_STRING, "%d files found", matchCount);
    }
    if ((mSearchControl->flags & SEARCH_INVERT_FILES) != 0) {
        g_strlcat(status->constantString, " [inv]", MAX_FILENAME_STRING);
    }
    if (mSearchControl->cancelSearch == TRUE) {
        g_strlcat(status->constantString, " [cancelled]", MAX_FILENAME_STRING);
    }
    
    gtk_statusbar_pop(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status));
    gtk_statusbar_push(mSearchControl->status, STATUSBAR_CONTEXT_ID(mSearchControl->status),
                       status->constantString);
    gtk_widget_queue_draw(GTK_WIDGET(mSearchControl->status));
    gdk_threads_leave ();
}

gboolean statMatchPhase(const gchar *tmpFullFileName, searchControl *mSearchControl)
{
  struct stat buf;
    
  g_lstat(tmpFullFileName, &buf);
  
  if ((mSearchControl->flags & SEARCH_MORETHAN_SET) != 0) {
      if (buf.st_size <= mSearchControl->moreThan) {
          return FALSE;
      }      
  }
  
  if ((mSearchControl->flags & SEARCH_LESSTHAN_SET) != 0) {
      if (buf.st_size >= mSearchControl->lessThan) {
          return FALSE;
      }      
  }
  
  if ((mSearchControl->flags & SEARCH_AFTER_SET) != 0) {
      if (difftime(buf.st_mtime, mSearchControl->after) <=0) {
          return FALSE;
      }
  }
  
  if ((mSearchControl->flags & SEARCH_BEFORE_SET) != 0) {
      if (difftime(mSearchControl->before, buf.st_mtime) <=0) {
          return FALSE;
      }      
  }
  
  return TRUE;
}

/*************************************************************
 *  Constructors and destructors
 *************************************************************/

void createSearchData(GObject *object, const gchar *dataName)
{
  searchData *mSearchData;
  
  /* Create pointer arrays */
  mSearchData = g_malloc(sizeof(searchData));
  
  mSearchData->pLocationArray = g_ptr_array_new();
  mSearchData->textMatchArray = g_ptr_array_new();
  mSearchData->fileNameArray = g_ptr_array_new();
  mSearchData->fullNameArray = g_ptr_array_new();
  mSearchData->lineMatchArray = g_ptr_array_new(); /* Only pointers to baseDirArray */
  
  /* Create string chunks */
  mSearchData->locationChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);
  mSearchData->fileSizeChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);
  mSearchData->mDateChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);
  mSearchData->fileTypeChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);
  mSearchData->textMatchChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);

  mSearchData->store = NULL;
  mSearchData->iter = g_malloc(sizeof(GtkTreeIter));
  
  /* Attach the data to the G_OBJECT */
  g_object_set_data_full(object, dataName, mSearchData, destroySearchData);

  return;
}

void destroySearchData(gpointer data)
{
  searchData *mSearchData = data;

  mSearchData->store = NULL; /* Clear the pointer to it.. */
  g_free(mSearchData->iter);
  mSearchData->iter = NULL;
  
  /* Destroy pointer arrays*/
  g_string_chunk_free(mSearchData->locationChunk);
  g_string_chunk_free(mSearchData->fileSizeChunk);
  g_string_chunk_free(mSearchData->mDateChunk);
  g_string_chunk_free(mSearchData->fileTypeChunk);
  g_string_chunk_free(mSearchData->textMatchChunk);

  /* Destroy string chunks */
  g_ptr_array_free(mSearchData->pLocationArray, FALSE); /* only pointers to baseDirArray */ 
  g_ptr_array_free(mSearchData->textMatchArray, TRUE);
  g_ptr_array_free(mSearchData->fileNameArray, TRUE);
  g_ptr_array_free(mSearchData->fullNameArray, TRUE);
  g_ptr_array_free(mSearchData->lineMatchArray, TRUE);
  
  g_free(mSearchData);
}

void createSearchControl(GObject *object, const gchar *dataName)
{
  searchControl *mSearchControl;

  /* Create pointer arrays*/
  mSearchControl = g_malloc(sizeof(searchControl));
  mSearchControl->flags = 0;
  mSearchControl->textSearchFlags = 0;
  mSearchControl->fileSearchFlags = 0;

  /* Attach the data to the G_OBJECT */
  g_object_set_data_full(object, dataName, mSearchControl, g_free); //destroySearchControl);

  return;
}

void createStatusbarData(GObject *object, const gchar *dataName)
{
  statusbarData *status;

  status = g_malloc(sizeof(statusbarData));
  status->statusbarChunk = g_string_chunk_new(MAX_FILENAME_STRING + 1);

  /* Attach the data to the G_OBJECT */
  g_object_set_data_full(object, dataName, status, destroyStatusbarData);

  return;
}

void destroyStatusbarData(gpointer data)
{
  statusbarData *status = data;
  g_string_chunk_free(status->statusbarChunk);
  g_free(status);
}

void createGKeyFile(GObject *object, const gchar *dataName)
{
  GKeyFile *keyString;
  keyString = g_key_file_new ();
  gchar *fullPath;

  if (!g_key_file_load_from_data_dirs(keyString,
                                     OPTIONS_SAVE_LOCATION,
                                     &fullPath,
                                     G_KEY_FILE_KEEP_COMMENTS,
                                      NULL)) {
    g_key_file_set_comment (keyString, NULL, NULL,
                            " " PACKAGE " settings auto-generated - Do not edit!", NULL);
  } else {
    g_object_set_data_full(object, "configFileSaveLocation", fullPath, g_free);
  }
  
  g_object_set_data_full(object, dataName, keyString, destroyGKeyFile);
}

void destroyGKeyFile(gpointer data)
{
  GKeyFile *keyString = data;

  if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mainWindowApp), "CONFIG_DISABLE_SAVE_CONFIG")) != CONFIG_DISABLE_SAVE_CONFIG) {
    storeGKeyFile(keyString);
  }
  g_key_file_free(keyString);
}



/*************************************************************
 *  library helpers
 *************************************************************/


void start_search_thread(GtkWidget *widget)
{
  pthread_t walk_tid;
  regex_t testRegEx;
  searchControl *mSearchControl;
  GObject *window1 = G_OBJECT(lookup_widget(widget, "window1"));
  GtkWidget *dialog;
  GDate date;
  const gchar *after = gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, "afterEntry")));
  const gchar *before = gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, "beforeEntry")));
  const gchar *moreThan = gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, "moreThanEntry")));
  const gchar *lessThan = gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, "lessThanEntry")));
  gchar *tmpChar;
  guint tmpInt;
  struct tm tptr;
  gdouble tmpDouble;
  gchar buffer[MAX_FILENAME_STRING + 1];
  gchar *endChar;
  gchar *tmpStr;
  GKeyFile *keyString = getGKeyFile(widget);;
  
  /* Create user data storage & control (automatic garbage collection) */
  createSearchData(window1, MASTER_SEARCH_DATA);
  createSearchControl(window1, MASTER_SEARCH_CONTROL);  
  
  mSearchControl = g_object_get_data(window1, MASTER_SEARCH_CONTROL);
  mSearchControl->cancelSearch = FALSE; /* reset cancel signal */
//  mSearchData = g_object_get_data(G_OBJECT(widget), MASTER_SEARCH_DATA);
  
  /* Store pointers to the GTK objects */
  mSearchControl->status = GTK_STATUSBAR(lookup_widget(widget, "statusbar1"));
  mSearchControl->listView = GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  mSearchControl->textView = GTK_TEXT_VIEW(lookup_widget(widget, "textview1"));
  mSearchControl->goButton = GTK_BUTTON(lookup_widget(widget, "playButton1"));
  mSearchControl->stopButton = GTK_BUTTON(lookup_widget(widget, "stopButton1"));
  mSearchControl->goButton1 = GTK_BUTTON(lookup_widget(widget, "playButton2"));
  mSearchControl->stopButton1 = GTK_BUTTON(lookup_widget(widget, "stopButton2"));
  mSearchControl->goButton2 = GTK_WIDGET(lookup_widget(widget, "playButton3"));
  mSearchControl->stopButton2 = GTK_WIDGET(lookup_widget(widget, "stopButton3"));
  //  mSearchControl->store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (mSearchControl->listWidget)));
  mSearchControl->textSearchRegEx = (gchar *)gtk_combo_box_get_active_text(GTK_COMBO_BOX(lookup_widget(widget, "containingText")));

  /* Set the flags up */
  mSearchControl->flags = 0; /* Disable all flags */
  if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "containingTextCheck")))) &&
      (*mSearchControl->textSearchRegEx != '\0')) {
      mSearchControl->flags |= SEARCH_TEXT_CONTEXT; /* Allow context switching */
  }
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "searchSubfoldersCheck")))) {
      mSearchControl->flags |= SEARCH_SUB_DIRECTORIES; /* Allow sub-directory searching */
  }
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "ignoreHiddenFiles")))) {
      mSearchControl->flags |= SEARCH_HIDDEN_FILES; /* Allow hidden searching */
  }  
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "notExpressionCheckFile")))) {
      mSearchControl->flags |= SEARCH_INVERT_FILES; /* Invert the search on File names e.g. find everything but abc */
  }  
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "matchCaseCheckFile")))) {
    mSearchControl->fileSearchFlags |= REG_ICASE;
  }
  if ((!g_key_file_has_key(keyString, "configuration", "configExtendedRegex", NULL)) || 
      (g_key_file_get_boolean(keyString, "configuration", "configExtendedRegex", NULL))) {
    mSearchControl->fileSearchFlags |= REG_EXTENDED;
    mSearchControl->textSearchFlags |= REG_EXTENDED;
  }

  mSearchControl->fileSearchFlags |= REG_NOSUB;
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "matchCaseCheckContents")))) {
    mSearchControl->textSearchFlags |= REG_ICASE;
  }
  mSearchControl->textSearchFlags |= REG_NEWLINE;
  /* mSearchControl->textSearchFlags |= REG_NOSUB; */
  

/* Read date strings */
  if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "afterCheck")))) &&
      ((after != NULL) && (after != '\0'))) {
      g_date_set_parse(&date, after);
      if (!g_date_valid(&date)) {
        dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_CLOSE,
                                         "Error! Invalid 'After' date - format as dd/mm/yyyy or dd mmm yy.");
        gtk_dialog_run (GTK_DIALOG (dialog));
        gtk_widget_destroy (dialog);
        return;
      }
      if (g_date_strftime(buffer, MAX_FILENAME_STRING, "%d %b %Y", &date) > 0) {
        gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "afterEntry")), buffer);
      }
      memset(&tptr, 0x00, sizeof(struct tm));
      tptr.tm_mday = date.day;
      tptr.tm_mon = (date.month - 1);
      tptr.tm_year = (date.year - 1900);
      mSearchControl->after = mktime(&tptr); 
      mSearchControl->flags |= SEARCH_AFTER_SET;
  }
  if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "beforeCheck")))) &&
      ((before != NULL) && (before != '\0'))) {
      g_date_set_parse(&date, before);
      if (!g_date_valid(&date)) {
          dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                           GTK_DIALOG_DESTROY_WITH_PARENT,
                                           GTK_MESSAGE_ERROR,
                                           GTK_BUTTONS_CLOSE,
                                           "Error! Invalid 'Before' date - format as dd/mm/yyyy or dd mmm yy.");
          gtk_dialog_run (GTK_DIALOG (dialog));
          gtk_widget_destroy (dialog);
          return;
      }
      if (g_date_strftime(buffer, MAX_FILENAME_STRING, "%d %b %Y", &date) > 0) {
        gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "beforeEntry")), buffer);
      }
      memset(&tptr, 0x00, sizeof(struct tm));
      tptr.tm_mday = date.day;
      tptr.tm_mon = (date.month - 1);
      tptr.tm_year = (date.year - 1900);
      mSearchControl->before = mktime(&tptr);
      mSearchControl->flags |= SEARCH_BEFORE_SET;
  }

/* Read file min/max size strings */
  if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "moreThanCheck")))) &&
      (moreThan != NULL)) {
      tmpDouble = strtod (moreThan, &endChar);
      if (tmpDouble <= 0) {
          dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                           GTK_DIALOG_DESTROY_WITH_PARENT,
                                           GTK_MESSAGE_ERROR,
                                           GTK_BUTTONS_CLOSE,
                                           "Error! MoreThan file size must be positive value");
          gtk_dialog_run (GTK_DIALOG (dialog));
          gtk_widget_destroy (dialog);
          return;
      }
      g_ascii_formatd (buffer, MAX_FILENAME_STRING, "%1.1f", tmpDouble);
      gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "moreThanEntry")), buffer);
      mSearchControl->moreThan = (gsize)(1024 * tmpDouble);
      mSearchControl->flags |= SEARCH_MORETHAN_SET;
  }
  if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "lessThanCheck")))) &&
      (lessThan != NULL)) {
      tmpDouble = strtod (lessThan, &endChar);
      
      if (tmpDouble <= 0) {
          dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                           GTK_DIALOG_DESTROY_WITH_PARENT,
                                           GTK_MESSAGE_ERROR,
                                           GTK_BUTTONS_CLOSE,
                                           "Error! LessThan file size must be positive value");
          gtk_dialog_run (GTK_DIALOG (dialog));
          gtk_widget_destroy (dialog);
          return;
      }
      g_ascii_formatd (buffer, MAX_FILENAME_STRING, "%1.1f", tmpDouble);
      gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "lessThanEntry")), buffer);
      mSearchControl->lessThan = (gsize)(1024 * tmpDouble);
      mSearchControl->flags |= SEARCH_LESSTHAN_SET;
  }


  /* Copy the text strings into the GTK objects */  
  mSearchControl->fileSearchRegEx = (gchar *)gtk_combo_box_get_active_text(GTK_COMBO_BOX(lookup_widget(widget, "fileName")));


  /* Read the look in directory name */
  mSearchControl->startingFolder = comboBoxReadCleanFolderName(GTK_COMBO_BOX(lookup_widget(widget, "lookIn")));
  // addUniqueRow(lookup_widget(widget, "lookIn"), mSearchControl->startingFolder);
  
  /* Test starting folder exists */
  if (*(mSearchControl->startingFolder) == '\0') {
    dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     "Error! Look In directory cannot be blank.");
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    return;
  }
  
  if (!g_file_test(mSearchControl->startingFolder, G_FILE_TEST_IS_DIR)) {
    dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     "Error! Look In directory is invalid.");
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    return;
  }

  /* Test fileSearchRegEx reg expression is valid */
  if (regcomp(&testRegEx,
              mSearchControl->fileSearchRegEx,
              mSearchControl->fileSearchFlags) != 0) {
    dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     "Error! Unable to compile File Name regular expression.");
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    return;
  }
  regfree(&testRegEx);

  /* Test fileSearchRegEx reg expression is valid */
  if (regcomp(&testRegEx,
              mSearchControl->textSearchRegEx,
              mSearchControl->textSearchFlags) != 0) {
    dialog = gtk_message_dialog_new (GTK_WINDOW(window1),
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     "Error! Unable to compile Containing Text regular expression.");
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    return;
  }
  regfree(&testRegEx);
  
  /* Store the text strings (once validated) into the combo boxes */
  addUniqueRow(lookup_widget(widget, "fileName"), mSearchControl->fileSearchRegEx);
  addUniqueRow(lookup_widget(widget, "containingText"), mSearchControl->textSearchRegEx);
  addUniqueRow(lookup_widget(widget, "lookIn"), mSearchControl->startingFolder);

  /* create the search thread */
  pthread_create (&walk_tid, NULL, walkDirectories, window1);
  
}


void stop_search_thread(GtkWidget *widget)
{
  searchControl *mSearchControl;
  GObject *window1 = G_OBJECT(lookup_widget(widget, "window1"));
  
  mSearchControl = g_object_get_data(window1, MASTER_SEARCH_CONTROL);

  mSearchControl->cancelSearch = TRUE;
}


gboolean
on_searchEntry_focus_in_event          (GtkWidget       *widget,
                                        GdkEventFocus   *event,
                                        gpointer         user_data)
{
  set_text_ok(widget);
  return FALSE;
}

void set_text_ok(GtkWidget *widget)
{
  GdkColor normalColour;

  normalColour.red = 0;
  normalColour.green = 0;
  normalColour.blue = 0;

  gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &normalColour);
  return;
}

void set_text_error(GtkWidget *widget)
{  
  GdkColor redHighlight;

  redHighlight.red = 0xFFFF;
  redHighlight.green = 0;
  redHighlight.blue = 0;  

  gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &redHighlight);
  return;
}

gboolean test_regex(gchar *search, guint testFlags)
{
  regex_t testRegEx;
  
  /* Test fileSearchRegEx reg expression is valid */
  if (regcomp(&testRegEx, search, testFlags) != 0) {
    return FALSE;
  }
  regfree(&testRegEx);
  return TRUE;
}

void initTextView( GtkTextView *widget)
{
  GtkTextBuffer *buffer;
  GtkTextTag *tag;
  
  buffer = gtk_text_view_get_buffer (widget);
  tag = gtk_text_buffer_create_tag (buffer, "word_highlight",
                                    "foreground", "blue",
                                    "underline", PANGO_UNDERLINE_SINGLE,
                                    "underline-set", TRUE,
                                    NULL);
  tag = gtk_text_buffer_create_tag (buffer, "no_context",
                                    "foreground", "darkGrey",
                                    "underline-set", FALSE,
                                    NULL);
  return;
}


void initTreeView(GtkTreeView *widget)
{
  
  GtkListStore *store;
  GtkCellRenderer *renderer, *folderRenderer;
  GtkTreeViewColumn *column;
  GtkTreeSelection *select;
  GtkTreeModel *sortedModel;

  /* Attach new list store object to treeview widget */
  store = gtk_list_store_new (N_COLUMNS,       /* Total number of columns */
                              G_TYPE_STRING,   /* File name               */
                              G_TYPE_STRING,   /* Location                */
                              G_TYPE_STRING,   /* File size               */
                              G_TYPE_STRING,   /* File Type               */
                              G_TYPE_STRING,   /* Modified date           */
                              G_TYPE_STRING,   /* Complete File Name      */
                              G_TYPE_STRING,   /* Match count (string)    */
                              G_TYPE_UINT,     /* File size in bytes      */
                              G_TYPE_UINT,     /* Modified date as int    */
                              G_TYPE_UINT,     /* Match count             */
                              G_TYPE_UINT);    /* Match index             */

  sortedModel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(store));
  gtk_tree_view_set_model (GTK_TREE_VIEW(widget), GTK_TREE_MODEL (sortedModel));
  g_object_unref(store);

  /* Create cell renderers */
  renderer = gtk_cell_renderer_text_new ();
  folderRenderer = gtk_cell_renderer_text_new ();
  g_object_set(folderRenderer,
               "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
               "ellipsize-set", TRUE,
               NULL);

  /* Add columns to the list */
  column = gtk_tree_view_column_new_with_attributes ("Name",
                                                     renderer,
                                                     "text", FILENAME_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_filename_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
    
  column = gtk_tree_view_column_new_with_attributes ("Location",
                                                     folderRenderer,
                                                     "text", LOCATION_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_location_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
    
  column = gtk_tree_view_column_new_with_attributes ("Size",
                                                     renderer,
                                                     "text", SIZE_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_size_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
    
  column = gtk_tree_view_column_new_with_attributes ("Type",
                                                     renderer,
                                                     "text", TYPE_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_type_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
    
  column = gtk_tree_view_column_new_with_attributes ("Modified",
                                                     renderer,
                                                     "text", MODIFIED_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_modified_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);

  column = gtk_tree_view_column_new_with_attributes ("Matches",
                                                     renderer,
                                                     "text", MATCHES_COUNT_STRING_COLUMN,
                                                     NULL);
  g_signal_connect ((gpointer) column, "clicked",
                    G_CALLBACK (on_column_matches_click),
                    NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);

  /* Set up select call back handler */
  /* Setup the selection handler */

  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
  gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
  g_signal_connect (G_OBJECT (select), "changed",
                    G_CALLBACK (tree_selection_changed_cb),
                    NULL);

  /* Allow clickable headers */
  g_object_set(widget,
               "headers-clickable", TRUE,
               NULL);

  return;
}

void columnClick(GtkWidget *widget, gint column)
{
  gint oldColumn;
  GtkSortType direction;
  GtkTreeView *listview= GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  GtkTreeSortable *sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(listview));
  GtkTreeViewColumn *treeviewcolumn;
  GList *columnList = gtk_tree_view_get_columns(GTK_TREE_VIEW(listview));
  gint i=0;

  /* Change the sorted column */
  if (GTK_IS_TREE_SORTABLE (sortable)) {
      gtk_tree_sortable_get_sort_column_id (sortable, &oldColumn, &direction);
      if ((oldColumn == column) && (direction == GTK_SORT_ASCENDING)) {
	  direction = GTK_SORT_DESCENDING;
      } else {
	  direction = GTK_SORT_ASCENDING;
      }
      gtk_tree_sortable_set_sort_column_id (sortable, column, direction);

      /* Update the treeview heading indicators */
      if (column == INT_MODIFIED_COLUMN) {
	  oldColumn = MODIFIED_COLUMN;
      } else if (column == INT_SIZE_COLUMN) {
        oldColumn = SIZE_COLUMN;
      } else if (column == MATCHES_COUNT_COLUMN)  {
        oldColumn = MATCHES_COUNT_STRING_COLUMN;
      } else {
	  oldColumn = column;
      }
      columnList = g_list_first (columnList);
      do {
	  treeviewcolumn = GTK_TREE_VIEW_COLUMN(columnList->data);
	  if (i == oldColumn) {
	      gtk_tree_view_column_set_sort_indicator(treeviewcolumn, TRUE);
	      gtk_tree_view_column_set_sort_order(treeviewcolumn, direction);
	  } else {
	      gtk_tree_view_column_set_sort_indicator(treeviewcolumn, FALSE);
	      gtk_tree_view_column_set_sort_order(treeviewcolumn, GTK_SORT_ASCENDING);
	  }
	  i++;
      } while ((columnList = g_list_next(columnList)) != NULL);
  }
}

void getSortMenuItem(gint *columnId, GtkSortType *sortOrder)
{
  GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(mainWindowApp), "treeview1"));
  GtkTreeSortable *sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(treeview));
  gtk_tree_sortable_get_sort_column_id (sortable, columnId, sortOrder);
}

void setSortMenuItem(gint columnId, GtkSortType order)
{
  GtkCheckMenuItem *toggle_button;
  switch(columnId) {
  case LOCATION_COLUMN:
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "location1"));
    break;
  case INT_SIZE_COLUMN:  /* Force to SIZE column */
  case SIZE_COLUMN: 
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "size1"));
    break;
  case TYPE_COLUMN:
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "type1"));
    break;
  case INT_MODIFIED_COLUMN: /* Force to MODIFIED column */
  case MODIFIED_COLUMN:
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "modified1"));
    break;
  case MATCHES_COUNT_STRING_COLUMN: /* Force to MATCHES column */
  case MATCHES_COUNT_COLUMN:
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "matches1"));
    break;
  case FILENAME_COLUMN: /* This is the default option */
  default:
    toggle_button = GTK_CHECK_MENU_ITEM(lookup_widget(GTK_WIDGET(mainWindowApp), "file_name1"));
    break;
  }
  
  /* Special case, only works first time a button is selected */
  if (order == GTK_SORT_DESCENDING) {
    gtk_check_menu_item_set_active (toggle_button, FALSE);
    gtk_check_menu_item_set_active (toggle_button, TRUE);
  }
  
  /* Hack to get the buttons working as expected */
  gtk_check_menu_item_set_active (toggle_button, FALSE);
  gtk_check_menu_item_set_active (toggle_button, TRUE);
}


void on_column_location_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{
  setSortMenuItem(LOCATION_COLUMN, -1);
}

void on_column_filename_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{
  setSortMenuItem(FILENAME_COLUMN, -1);
}

void on_column_type_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{ 
  setSortMenuItem(TYPE_COLUMN, -1);
}

void on_column_modified_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{
  setSortMenuItem(MODIFIED_COLUMN, -1);
}

void on_column_matches_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{
  setSortMenuItem(MATCHES_COUNT_STRING_COLUMN, -1);
}

void on_column_size_click (GtkTreeViewColumn *treeviewcolumn,
                               gpointer user_data)
{
  setSortMenuItem(SIZE_COLUMN, -1);
}

void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
{
  GtkWidget *textBox;
  GtkTextBuffer *buffer;
  searchData *mSearchData; /* Master search data */
  lineMatch *newTextMatch, *prevTextMatch = NULL;
  GtkTreeIter iter;
  GtkTreeModel *model;
  gchar *fullFileName, *size, *mdate, tmpString[MAX_FILENAME_STRING + 1];
  gchar *tmpString2;
  GtkTextIter txtIter;
  GtkTextIter start, end;
  gsize count, tmpCount;
  guint matchIndex;
  gint i, lineCount = 0;
  GObject *window1;
  gboolean setWordWrap;
  gchar *t1;
  
  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    textBox = lookup_widget(GTK_WIDGET(gtk_tree_selection_get_tree_view(selection)), "textview1");
    window1 = G_OBJECT(lookup_widget(GTK_WIDGET(textBox), "window1"));
    setWordWrap = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(lookup_widget(textBox, "word_wrap1")));
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textBox), setWordWrap);

    g_assert(textBox != NULL);
    g_assert(window1 != NULL);
    
    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textBox)); 
    g_assert(buffer != NULL);
    gtk_text_buffer_set_text(buffer, "", -1); /* Clear text! */
    gtk_text_buffer_get_start_iter(buffer, &txtIter); /* Find start iter.. */

    /* Get global data from the text view widget pointer */
    mSearchData = g_object_get_data(window1, MASTER_SEARCH_DATA);
    g_assert(mSearchData != NULL);
    gtk_tree_model_get (model, &iter, MATCHES_COUNT_COLUMN, &count, -1);
    if (count > 0) {
      gtk_tree_model_get (model, &iter, FULL_FILENAME_COLUMN, &fullFileName, -1); 
      gtk_tree_model_get (model, &iter, SIZE_COLUMN, &size, -1);
      gtk_tree_model_get (model, &iter, MODIFIED_COLUMN, &mdate, -1);
      gtk_tree_model_get (model, &iter, MATCH_INDEX_COLUMN, &matchIndex, -1);

      g_assert(fullFileName != NULL);
      g_assert(size != NULL);
      g_assert(mdate != NULL);
      
      tmpString2 = g_strconcat(fullFileName, " (", size, " ", mdate, ")\n", NULL);
      gtk_text_buffer_insert (buffer, &txtIter, tmpString2, -1);
      g_free(tmpString2);
      
      for (i=0; i<count; i++) {
        //        = g_ptr_array_index(mSearchData->lineMatchArray, (i + matchIndex));
        newTextMatch = g_ptr_array_index(mSearchData->lineMatchArray, (i + matchIndex));
        g_assert(newTextMatch != NULL);
        
        if ((prevTextMatch == NULL) ||
            (prevTextMatch->lineNum != newTextMatch->lineNum)) {          
        
          tmpCount = g_snprintf(tmpString, 256, "%d. ", newTextMatch->lineNum);
          gtk_text_buffer_insert (buffer, &txtIter, tmpString, -1);
          //          t1 = g_strescape(newTextMatch->pLine, "\"\\\t\n\b\f\r");
          //         gtk_text_buffer_insert (buffer, &txtIter, t1, -1);
          //          g_free(t1);
          gtk_text_buffer_insert (buffer, &txtIter, newTextMatch->pLine, -1);
          gtk_text_buffer_insert (buffer, &txtIter, "\n", -1);
          
          lineCount++;
        }
        
        gtk_text_buffer_get_iter_at_line_offset (buffer, &start, lineCount, (tmpCount + newTextMatch->offsetStart));
        gtk_text_buffer_get_iter_at_line_offset (buffer, &end, lineCount, (tmpCount + newTextMatch->offsetEnd));
        gtk_text_buffer_apply_tag_by_name(buffer, "word_highlight", &start, &end);

        prevTextMatch = newTextMatch;
      }
      g_assert(fullFileName != NULL);
      g_assert(size != NULL);
      g_assert(mdate != NULL);
      
      g_free (fullFileName);
      g_free (size);
      g_free (mdate);
    } else { /* Print warning out just to fill the space */
      gtk_tree_model_get (model, &iter, FULL_FILENAME_COLUMN, &fullFileName, -1); 
      gtk_tree_model_get (model, &iter, SIZE_COLUMN, &size, -1);
      gtk_tree_model_get (model, &iter, MODIFIED_COLUMN, &mdate, -1);
      gtk_tree_model_get (model, &iter, MATCH_INDEX_COLUMN, &matchIndex, -1);

      tmpString2 = g_strconcat(fullFileName, " (", size, " ", mdate, ")\n", NULL);
      gtk_text_buffer_insert (buffer, &txtIter, tmpString2, -1);
      g_free(tmpString2);

      gtk_text_buffer_insert_with_tags_by_name (buffer, &txtIter, STR_NO_CONTEXT_SEARCH_DONE, -1, "no_context", NULL);;
    }
  }
}


void undo_popup_menu(GtkWidget *attach_widget, GtkMenu *menu)
{  
  gtk_widget_destroy(GTK_WIDGET(menu));
}


void do_popup_menu (GtkWidget *widget, GdkEventButton *event)
{
  GtkWidget *menu;
  int button, event_time;
  
  menu = create_menu1();
  
  if (event) {
    button = event->button;
    event_time = event->time;
  } else {
    button = 0;
    event_time = gtk_get_current_event_time ();
  }
  
  gtk_menu_attach_to_widget (GTK_MENU (menu), widget, undo_popup_menu);
  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 
                  event->button, event->time);
  return;
}

void detachMenu(GtkMenuItem *menuitem)
{
  GtkWidget *treeView = lookup_widget(GTK_WIDGET(menuitem), "treeview1");
  GList *menuList = gtk_menu_get_for_attach_widget (treeView);
  gtk_menu_detach(menuList->data); /* always first item */
}

GKeyFile *getGKeyFile(GtkWidget *widget)
{
  GObject *window1 = G_OBJECT(lookup_widget(GTK_WIDGET(widget), "window1"));
  GKeyFile *keyString = g_object_get_data(window1, MASTER_OPTIONS_DATA);
  return keyString;
}

void storeGKeyFile(GKeyFile *keyString)
{
  gsize length;
  gchar *outText = g_key_file_to_data (keyString, &length, NULL);
  gchar *folderName;
  gchar *fileName;
  GError *error = NULL;
  gchar *trueConfigFile = g_object_get_data(G_OBJECT(mainWindowApp), "configFileSaveLocation");
  GtkWidget *okCancelDialog;
  
  if (g_key_file_has_key(keyString, "configuration", "configPromptSave", NULL) &&
      (g_key_file_get_boolean(keyString, "configuration", "configPromptSave", NULL))) {
    okCancelDialog = gtk_message_dialog_new(GTK_WINDOW(mainWindowApp), (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                            GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
                                            "About to overwrite save configuration. Do you want to proceed?");
    if (gtk_dialog_run(GTK_DIALOG(okCancelDialog)) != GTK_RESPONSE_OK) {
      return;
    }
  }
    
  folderName = g_strconcat(g_get_user_data_dir(), "/", OPTIONS_SAVE_FOLDER, NULL);
  fileName = g_strconcat(g_get_user_data_dir(), "/", OPTIONS_SAVE_LOCATION, NULL);

  if (trueConfigFile == NULL) {  
    g_object_set_data_full(G_OBJECT(mainWindowApp), "configFileSaveLocation", g_strdup(fileName), g_free);
    
    trueConfigFile = fileName;
  }
  
  if (!g_file_set_contents2 (trueConfigFile, outText, length, &error)) {

    /* Unable to immediately write to file, so attempt to recreate folders */
    if (mkFullDir(folderName, S_IRWXU) == 0) {
      g_error_free(error);
      error = NULL;
      if (!g_file_set_contents2 (fileName, outText, length, &error)) { 
        g_print("Error saving %s: %s!\n", fileName, error->message);
        g_error_free(error);
        error = NULL;
      }  
      /* Otherwise, simply print error to terminal - user will have to fix if they want it */
    } else {
      g_print("Error saving %s: %s (%s)!\n", fileName, error->message, strerror(errno));
      g_error_free(error);
      error = NULL;
    }
  }
  
  g_free(outText);
  g_free(fileName);
  g_free(folderName);
}

void
openUrlLinkFunc (GtkAboutDialog *about, const gchar *link, gpointer data)
{
  /* TODO: Call up configuration if syscall returns FALSE */
  SMsyscall(link, BROWSER_LIST);
}

gboolean SMsyscall (const gchar *address, syscallType type)
{
  GKeyFile *keyString = getGKeyFile(GTK_WIDGET(mainWindowApp));
  gchar *executable, *attributes;
  gchar *s1, *s2;
  gboolean retVal = FALSE;
  GtkWidget *okDialog;

  g_assert(address != NULL);
  g_assert((type == BROWSER_LIST) ||
           (type == TEXTEDITOR_LIST) ||
           (type == FILEEXPLORER_LIST));

  /* Read in executable string, plus attributes*/
  switch (type) {
  case BROWSER_LIST:
    if ((!g_key_file_has_key(keyString, "configuration", "configWebBrowser", NULL)) || 
          (!g_key_file_has_key(keyString, "configuration", "configWebBrowserAttributes", NULL))) {
      okDialog = gtk_message_dialog_new(GTK_WINDOW(mainWindowApp), (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                        GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
                                        "Run configuration to set default web-browser:\n\"%s\"",address);
      gtk_dialog_run (GTK_DIALOG(okDialog));
      gtk_widget_destroy(GTK_WIDGET(okDialog));
      return -1;
    }
    executable = g_key_file_get_string (keyString, "configuration", "configWebBrowser", NULL);
    attributes = g_key_file_get_string (keyString, "configuration", "configWebBrowserAttributes", NULL);
    break;
  case TEXTEDITOR_LIST: 
    if ((!g_key_file_has_key(keyString, "configuration", "configTextEditor", NULL)) || 
          (!g_key_file_has_key(keyString, "configuration", "configTextEditorAttributes", NULL))) {
      okDialog = gtk_message_dialog_new(GTK_WINDOW(mainWindowApp), (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                        GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
                                        "Run configuration to set default text editor:\n\"%s\"",address);
      gtk_dialog_run (GTK_DIALOG(okDialog));
      gtk_widget_destroy(GTK_WIDGET(okDialog));
      return -1;
    }
    executable = g_key_file_get_string (keyString, "configuration", "configTextEditor", NULL);
    attributes = g_key_file_get_string (keyString, "configuration", "configTextEditorAttributes", NULL);
    break;
  case FILEEXPLORER_LIST:
    if ((!g_key_file_has_key(keyString, "configuration", "configFileExplorer", NULL)) || 
          (!g_key_file_has_key(keyString, "configuration", "configFileExplorerAttributes", NULL))) {
      okDialog = gtk_message_dialog_new(GTK_WINDOW(mainWindowApp), (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                        GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
                                        "Run configuration to set default directory/folder browser:\n\"%s\"", address);
      gtk_dialog_run (GTK_DIALOG(okDialog));
      gtk_widget_destroy(GTK_WIDGET(okDialog));
      return -1;
    }
    executable = g_key_file_get_string (keyString, "configuration", "configFileExplorer", NULL);
    attributes = g_key_file_get_string (keyString, "configuration", "configFileExplorerAttributes", NULL);
    break;
  default:
    return -1;
  }

  /* Execute the command (replacing %f with filename) */
  s1 = replaceAttributeString(attributes, address);
  if (s1 != NULL) {
    s2 = g_strconcat(executable, s1, NULL);
    g_free(s1);
    if (s2 != NULL) {
      g_spawn_command_line_async (s2, NULL);
      g_free(s2);
      retVal = TRUE;
    }
  }
 
  /* Clean up */
  g_free(executable);
  g_free(attributes);
  return retVal;
}


void realizeWindow(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  gint *width_height;
  gsize length;
  if (g_key_file_has_key(keyString, group, name, NULL)) {
    width_height = g_key_file_get_integer_list (keyString, group, name, &length, NULL);
    if (length == 2) {
      gtk_window_set_default_size (GTK_WINDOW(lookup_widget(widget,name)),
                                   width_height[0], width_height[1]);
    }
  }
}


void unrealizeWindow(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  gint width_height[2];
  gtk_window_get_size (GTK_WINDOW(lookup_widget(widget, name)),
                       &width_height[0], &width_height[1]);
  //  gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(lookup_widget(widget, name)));
  g_key_file_set_integer_list (keyString, group, name, width_height, 2);
}


void realizeFileDialog(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
    gchar *filename;
    GtkWidget *dialog = lookup_widget(widget, name);

    if (g_key_file_has_key(keyString, group, name, NULL)) {
        filename = g_key_file_get_string (keyString, group, name, NULL);
        if (filename != NULL) {
            gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filename);
            g_free(filename);
        }
    }

    return;
}

void unrealizeFileDialog(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
    gchar *filename;
    GtkWidget *dialog = lookup_widget(widget, name);
    
    filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
    if (filename != NULL) {
        g_key_file_set_string (keyString, group, name, filename);
        g_free(filename);
    }
    return;
}

void realizeTextviewFont(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

    gchar *newFont;
    PangoFontDescription *desc;
    
    PangoContext* context = gtk_widget_get_pango_context (widget);
    
    g_assert(context != NULL);
    
    if (g_key_file_has_key(keyString, group, name, NULL)) {
        newFont = g_key_file_get_string (keyString, group, name, NULL);
        if (newFont != NULL) {
            desc = pango_font_description_from_string(newFont);
            if (desc != NULL) {   
              gtk_widget_modify_font (widget, desc);
              pango_font_description_free(desc);
            }
            g_free(newFont);
        }
    }
}

void unrealizeTextviewFont(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

    
    PangoContext* context = gtk_widget_get_pango_context (widget);
    PangoFontDescription *desc = pango_context_get_font_description(context);    
    gchar *newFont = pango_font_description_to_string(desc);

    if (newFont != NULL) {  
      g_key_file_set_string (keyString, group, name, newFont);
      g_free(newFont);
    }
    

}

void realizeTextviewHighlight(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

    gchar *newColor;
    GdkColor cp;
    //    gint cp_num;
    GtkTextBuffer* textBuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
    GtkTextTagTable* tagTable = gtk_text_buffer_get_tag_table(textBuf);
    GtkTextTag* tag = gtk_text_tag_table_lookup(tagTable, "word_highlight");
    
    g_assert(tag != NULL);
    g_assert(tagTable != NULL);
    g_assert(textBuf != NULL);    
    
    if (g_key_file_has_key(keyString, group, name, NULL)) {
        newColor = g_key_file_get_string (keyString, group, name, NULL);
        if (newColor != NULL) {
            if (gdk_color_parse (newColor, &cp)) {
                g_object_set( G_OBJECT(tag), "foreground-gdk", &cp, NULL);
            }
            g_free(newColor);
        }
    }

}

void unrealizeTextviewHighlight(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

    gchar *newColor;
    GdkColor *cp;
    GtkTextBuffer* textBuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
    GtkTextTagTable* tagTable = gtk_text_buffer_get_tag_table(textBuf);
    GtkTextTag* tag = gtk_text_tag_table_lookup(tagTable, "word_highlight");

    g_object_get( G_OBJECT(tag), "foreground-gdk", &cp, NULL);
    newColor = gtk_color_selection_palette_to_string(cp, 1);
    
    g_key_file_set_string (keyString, group, name, newColor);
    gdk_color_free(cp);
    g_free(newColor);

}

void realizeMenuCheck(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
    if (g_key_file_has_key(keyString, group, name, NULL)) {
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(lookup_widget(widget, name)),
                                     g_key_file_get_boolean (keyString, group, name, NULL));
    }
}

void unrealizeMenuCheck(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
    g_key_file_set_boolean (keyString, group, name,                       
                            gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(lookup_widget(widget, name))));
}

void realizeCombo(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  gchar *tmpString;
  
  if (g_key_file_has_key(keyString, group, name, NULL)) {
    tmpString = g_key_file_get_string (keyString, group, name, NULL);
    if (tmpString != NULL) {
      gtk_combo_box_prepend_text(GTK_COMBO_BOX(lookup_widget(widget, name)), tmpString);
      gtk_combo_box_set_active(GTK_COMBO_BOX(lookup_widget(widget, name)), 0);
      g_free(tmpString);
    }
  }
}

void unrealizeCombo(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  gchar *tmpString;

  tmpString = gtk_combo_box_get_active_text(GTK_COMBO_BOX(lookup_widget(widget, name)));
  if (tmpString != NULL) {
    g_key_file_set_string (keyString, group, name, tmpString);
    g_free(tmpString);
  }
}

void realizeComboModel(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
    gchar **tmpString;
    gchar *readString;
    gsize length;
    gint i;
    gint activeRow = 0;
    GtkComboBox *comboBox = GTK_COMBO_BOX(lookup_widget(widget, name));
    
    g_assert(comboBox != NULL);

    if (g_key_file_has_key(keyString, group, name, NULL)) {
        tmpString = g_key_file_get_string_list (keyString, group, name, &length, NULL);
        if (tmpString != NULL) {
            for (i=length; i>0; i--) {
                addUniqueRow((lookup_widget(widget, name)), tmpString[i-1]);
            }
            g_strfreev(tmpString);
        }
    }

    /* Retrieve active row */
    readString = g_strconcat(name, "-active", NULL);
    g_assert(readString != NULL);
    activeRow = g_key_file_get_integer (keyString, group, readString, NULL);
    g_free(readString);
    if ((activeRow >= 0) && (activeRow < length)) {   
        gtk_combo_box_set_active (comboBox, activeRow);
    }    
}

void unrealizeComboModel(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  GtkTreeIter iter;
  gchar *readString = NULL;
  GtkComboBox *comboBox = GTK_COMBO_BOX(lookup_widget(widget, name));
  GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model (comboBox));
  gchar **outString;
  gint i=0;
  gint activeRow = gtk_combo_box_get_active (comboBox); 
  g_assert(store != NULL);
  g_assert(comboBox != NULL);

  /* Store active row */
  readString = g_strconcat(name, "-active", NULL);
  g_key_file_set_integer (keyString, group, readString, activeRow);
  g_free(readString);
  
  /* Find first, and then loop through all until duplicate found */
  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) {
    outString = malloc((1 + gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL)) * sizeof(gchar *));
      do {
          gtk_tree_model_get (GTK_TREE_MODEL(store), &iter,
                              0, &readString,
                              -1);
          if (readString != NULL) {
              outString[i++] = readString;
          }
      } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
      outString[i] = NULL; /* Terminate array */
      
      g_key_file_set_string_list (keyString, group, name, (const gchar**)outString, i);

      /* Clean up */
      g_strfreev(outString);
  }
}

void realizeTreeview(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name, gboolean autoColumnWidth)
{
  gchar *fullName;
  GtkCheckMenuItem *toggle_button;
  gint colNum;
  GtkSortType colOrder;
  GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  GList *allColumns = gtk_tree_view_get_columns(treeview);
  GtkTreeViewColumn *column;

  /* restore sort column */
  fullName = g_strconcat(name, "Sortcol", NULL);
  if (g_key_file_has_key(keyString, group, fullName, NULL)) {
    colNum = g_key_file_get_integer (keyString, group, fullName, NULL);
  }
  g_free(fullName);

  /* restore sort order */
  fullName = g_strconcat(name, "SortcolOrder", NULL);
  if (g_key_file_has_key(keyString, group, fullName, NULL)) {
    colOrder = g_key_file_get_integer (keyString, group, fullName, NULL);
  }
  g_free(fullName);
  setSortMenuItem(colNum, colOrder);
  
  /* restore column widths */
  allColumns = g_list_first(allColumns);
  do {
    column =  allColumns->data;
    
    fullName = g_strconcat(name, gtk_tree_view_column_get_title(column), NULL);
    if (g_key_file_has_key(keyString, group, fullName, NULL)) {
      colNum = g_key_file_get_integer (keyString, group, fullName, NULL);
      if (autoColumnWidth) {
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
      } else {
        if (colNum <= 1) {
          colNum = 50;
        }
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
        gtk_tree_view_column_set_fixed_width(column, colNum);
        gtk_tree_view_column_set_resizable (column, TRUE);
      }
    }
    g_free(fullName);
  } while ((allColumns = g_list_next(allColumns)) != NULL);

  g_list_free(allColumns);
}

void unrealizeTreeview(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name, gboolean autoColumnWidth)
{
  gchar *fullName;
  GtkCheckMenuItem *toggle_button;
  gint colNum;
  GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  GList *allColumns = gtk_tree_view_get_columns(treeview);
  GtkTreeViewColumn *column;
  GtkSortType sortOrder;
  
  /* save sort column + order */
  getSortMenuItem(&colNum, &sortOrder);

  fullName = g_strconcat(name, "Sortcol", NULL);
  g_key_file_set_integer (keyString, group, fullName, colNum);
  g_free(fullName);
  
  fullName = g_strconcat(name, "SortcolOrder", NULL);
  g_key_file_set_integer (keyString, group, fullName, sortOrder);
  g_free(fullName);

  /* Set all columns width (or auto size status) */
  allColumns = g_list_first(allColumns);
  do {
    column =  allColumns->data;
    
    fullName = g_strconcat(name, gtk_tree_view_column_get_title(column), NULL);
    if (!autoColumnWidth) { /* Only store if manual column sizing */
      colNum = gtk_tree_view_column_get_width(column);
      g_key_file_set_integer (keyString, group, fullName, colNum);
    }
    g_free(fullName);
  } while ((allColumns = g_list_next(allColumns)) != NULL);

  g_list_free(allColumns);
}

void realizeString(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  gchar *tmpString;
  
  if (g_key_file_has_key(keyString, group, name, NULL)) {
    tmpString = g_key_file_get_string (keyString, group, name, NULL);
    if (tmpString != NULL) {
      gtk_entry_set_text(GTK_ENTRY(lookup_widget((widget), name)), tmpString);
      g_free(tmpString);
     }
  }

}

void unrealizeString(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  g_key_file_set_string (keyString, group, name,
                         gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, name))));

}

gboolean realizeFileButton(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  GtkFileChooser *chooser = GTK_FILE_CHOOSER(lookup_widget(widget, name));
  gchar *tmpString;
  gboolean retVal;
  
  if (g_key_file_has_key(keyString, group, name, NULL)) {
    tmpString = g_key_file_get_string (keyString, group, name, NULL);
    if (tmpString != NULL) {
      retVal = gtk_file_chooser_set_filename (chooser, tmpString);
      g_free(tmpString);
      return retVal;
    }
  }
  return FALSE;
}

void unrealizeFileButton(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{
  GtkFileChooser *chooser = GTK_FILE_CHOOSER(lookup_widget(widget, name));
  gchar *tmpString = gtk_file_chooser_get_filename (chooser);

  if (tmpString != NULL) {
    g_key_file_set_string (keyString, group, name, tmpString);
    g_free(tmpString);
  }
}

void realizeToggle(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  if (g_key_file_has_key(keyString, group, name, NULL)) {
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, name)),
                                 g_key_file_get_boolean (keyString, group, name, NULL));
  }

}

void unrealizeToggle(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  g_key_file_set_boolean (keyString, group, name,
                          gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, name))));

}

void realizeNotebook(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  if (g_key_file_has_key(keyString, group, name, NULL)) {
    gtk_notebook_set_current_page(GTK_NOTEBOOK(widget),
                                  g_key_file_get_integer (keyString, group, name, NULL));
  }

}

void unrealizeNotebook(GtkWidget *widget, GKeyFile *keyString, const gchar *group, const gchar *name)
{

  g_key_file_set_integer (keyString, group, name,
                          gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)));

}


gint stringCompareFunc (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)

{
  gchar *a_string, *b_string;
  guint col = (0xFFFFFFFF & (guint)user_data);
  gint retVal = 0;
  
  gtk_tree_model_get (model, a, col, &a_string, -1);
  gtk_tree_model_get (model, b, col, &b_string, -1);
  
  retVal = g_ascii_strcasecmp(a_string, b_string);
  
  g_free(a_string);
  g_free(b_string);
  
  return retVal;
}

void initComboBox(GtkWidget *comboBox)
{
  GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); /* Create a simple 1-wide column */
  gtk_combo_box_set_model (GTK_COMBO_BOX(comboBox), GTK_TREE_MODEL(store));
  g_object_unref(store);
}

gboolean addUniqueRow(GtkWidget *comboBox, const gchar *entry)
{
    GtkTreeIter iter;
    gchar *readString;
    GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model (GTK_COMBO_BOX(comboBox)));

    /* Find first, and then loop through all until duplicate found */
    if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) {
        do {
            gtk_tree_model_get (GTK_TREE_MODEL(store), &iter,
                                0, &readString,
                                -1);
            if (strcmp(readString, entry) == 0) {
                g_free(readString);
                gtk_combo_box_set_active_iter (GTK_COMBO_BOX(comboBox), &iter);
                return FALSE;
            }
            g_free(readString);    
        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
    }

/* Else unique, so add a new row to the model */
    gtk_list_store_prepend (store, &iter);
    gtk_list_store_set (store, &iter,
                        0, entry,
                        -1);
    gtk_combo_box_set_active_iter (GTK_COMBO_BOX(comboBox), &iter);
    return TRUE;
}
void clearComboBox(GtkWidget *comboBox)
{
    GtkTreeIter iter;
    gchar *readString;
    GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model (GTK_COMBO_BOX(comboBox)));
    g_assert(store != NULL);

    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(comboBox), &iter)) {
        return;
    }
    
    gtk_tree_model_get (GTK_TREE_MODEL(store), &iter,
                        0, &readString,
                        -1);
    gtk_list_store_clear (GTK_LIST_STORE(store));
    if (readString != NULL) {
        gtk_list_store_prepend (store, &iter);
        gtk_list_store_set (store, &iter,
                            0, readString,
                            -1);
        g_free(readString);
        gtk_combo_box_set_active_iter (GTK_COMBO_BOX(comboBox), &iter);
    }
}

void refreshTestResults(GtkWidget *widget)
{
    GtkTextBuffer *tBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(lookup_widget(widget, "SampleTextView")));
    GtkTextIter startIter, endIter;
    gtk_text_buffer_get_start_iter (tBuffer, &startIter);
    gtk_text_buffer_get_end_iter (tBuffer, &endIter);
    gchar *sampleText = gtk_text_buffer_get_text (tBuffer,
                                                  &startIter,
                                                  &endIter,
                                                  FALSE);
    regmatch_t subMatches[MAX_SUB_MATCHES];
    const gchar *testExpression = gtk_entry_get_text(GTK_ENTRY(lookup_widget(widget, "testEntry")));
    regex_t test;
    gint i;
    gchar test2[256+1];        
    gint errorCode;
    gchar *charMatch = sampleText;
    gsize intMatch = 0;
    gint matchCount = 0;
    GtkEntry *status= GTK_ENTRY(lookup_widget(widget, "testResultStatus"));
    
    /* Clear tags, and compile regex */
    gtk_entry_set_text(status, "");
    gtk_text_buffer_remove_all_tags (tBuffer, &startIter, &endIter);
    errorCode = regcomp(&test, testExpression, REG_ICASE | REG_EXTENDED | REG_NEWLINE);
    if (errorCode != 0) {
        regerror (errorCode, &test, test2, 256);
        gtk_entry_set_text(status, test2);
        g_free(sampleText);
        return;
    }

    
    /* Find matches */
    while (regexec(&test, charMatch, MAX_SUB_MATCHES, subMatches, 0) == 0) {
      
      /* Highlight code */
#ifdef NOT_YET
      for (i=0; i<MAX_SUB_MATCHES; i++) {
#endif
        i=0;
        if (subMatches[i].rm_so == -1) {
          break;
        }
        gtk_text_buffer_get_iter_at_offset (tBuffer, &startIter, intMatch + subMatches[i].rm_so);
        gtk_text_buffer_get_iter_at_offset (tBuffer, &endIter, intMatch + subMatches[i].rm_eo);
        switch(i) {
#ifdef NOT_YET
        case 0:
          gtk_text_buffer_apply_tag_by_name (tBuffer, "word_highlight", &startIter, &endIter);
          break;
#endif
        default:
          gtk_text_buffer_apply_tag_by_name (tBuffer, "word_highlight", &startIter, &endIter);
          break;
        }
#ifdef NOT_YET
      }
#endif
      matchCount ++;
      intMatch += subMatches[0].rm_eo;
      charMatch += subMatches[0].rm_eo;
    }
    
    if (matchCount == 0) {
      gtk_entry_set_text(status, "Did not match any.");
    } else if (matchCount == 1) {
      gtk_entry_set_text(status, "Found 1 match.");
    } else {
      charMatch = g_malloc(MAX_STRING_SIZE + 1);
      g_snprintf(charMatch, MAX_STRING_SIZE, "Found %d matches.", matchCount);
      gtk_entry_set_text(status, charMatch);
      g_free(charMatch);
    }

    g_free(sampleText);
    regfree(&test);
}

void updateTypeChangeEntry(GtkComboBox *combobox, GtkWidget *entry, GtkWidget *repeat)
{
    switch (gtk_combo_box_get_active (combobox)) {
        case REGWIZ_THE_CHAR:
            gtk_widget_set_sensitive (entry, TRUE);
            gtk_widget_set_sensitive(repeat, TRUE);
            gtk_entry_set_max_length (GTK_ENTRY(entry), 1);
            break;
            
        case REGWIZ_ANY_ONE_CHAR:
        case REGWIZ_ANY_CHAR_EXCEPT:
        case REGWIZ_THE_PHRASE:
            gtk_widget_set_sensitive(entry, TRUE);
            gtk_widget_set_sensitive(repeat, TRUE);
            gtk_entry_set_max_length (GTK_ENTRY(entry), 0);
            break;

        case REGWIZ_ANY_CHAR:
        case REGWIZ_SPACE:
        case REGWIZ_ANY_NUMERIC:
        case REGWIZ_ANY_TEXT:
            gtk_widget_set_sensitive(repeat, TRUE);
            gtk_widget_set_sensitive(entry, FALSE);
            break;
            
        case REGWIZ_DONT_KNOW:
        default: 
            gtk_widget_set_sensitive(entry, FALSE);
            gtk_widget_set_sensitive(repeat, FALSE);
            break;
    }
}

void updateRegExWizard(GtkWidget *widget)
{
    gint i = 0;
    GtkComboBox *type;
    GtkEntry *entry;
    GtkComboBox *repeat;
    GtkEntry *output = GTK_ENTRY(lookup_widget(widget, "resultExp"));
    GtkTreeView *treeView = GTK_TREE_VIEW(lookup_widget(widget, "midTreeView"));
    GtkTreeModel *treemodel = gtk_tree_view_get_model (treeView);
    GtkTreeIter iter;
    gint iType, iRepeat;
    gchar *pEntry;
    gchar **strArray, *tmpStr, *D;
    gint counter = 0;
    gint treeSize = 0;
    
    g_assert(treeView != NULL);
    
    if (treemodel == NULL) {
        return;
    }

    treeSize = gtk_tree_model_iter_n_children (treemodel, NULL);
    strArray = g_malloc((3 + treeSize) * sizeof(gchar *));
    
    type = GTK_COMBO_BOX(lookup_widget(widget, "startType"));
    entry = GTK_ENTRY(lookup_widget(widget, "startEntry"));
    repeat = GTK_COMBO_BOX(lookup_widget(widget, "startOccurance"));
    tmpStr = makeInterimRegex (type, entry, repeat, REGWIZ_START_TYPE);
    if (tmpStr != NULL) {
        strArray[counter++] = tmpStr;
    }
    
    type = GTK_COMBO_BOX(lookup_widget(widget, "midType"));
    entry = GTK_ENTRY(lookup_widget(widget, "midEntry"));
    repeat = GTK_COMBO_BOX(lookup_widget(widget, "midOccurance"));
    tmpStr = makeInterimRegex (type, entry, repeat, -1);
    if (tmpStr != NULL) {
        gtk_widget_set_sensitive(lookup_widget(widget, "addMidContents"), TRUE);
        g_free(tmpStr);
    } else {
        gtk_widget_set_sensitive(lookup_widget(widget, "addMidContents"), FALSE);
    }
    
    if (gtk_tree_model_get_iter_first   (treemodel,
                                         &iter)) {
        do {
            gtk_tree_model_get (treemodel, &iter,
                                REGEX_TYPE_INT_COLUMN, &iType,
                                REGEX_ENTRY_COLUMN, &pEntry,
                                REGEX_REPEAT_INT_COLUMN, &iRepeat,
                                -1);
            tmpStr = makeInterimRegex2 (widget, iType, pEntry, iRepeat, -1);
            if (tmpStr != NULL) {
                strArray[counter++] = tmpStr;
            }
            g_free(pEntry);
        } while (gtk_tree_model_iter_next(treemodel, &iter));
    }
    
    type= GTK_COMBO_BOX(lookup_widget(widget, "endType"));
    entry= GTK_ENTRY(lookup_widget(widget, "endEntry"));
    repeat = GTK_COMBO_BOX(lookup_widget(widget, "endOccurance"));
    tmpStr = makeInterimRegex (type, entry, repeat, REGWIZ_END_TYPE);
    if (tmpStr != NULL) {
        strArray[counter++] = tmpStr;
    }
    
    strArray[counter] = NULL;
    D = g_strjoinv ("", strArray);

    while (strArray[i] != NULL) {
        g_free(strArray[i++]);
    } 

    if ((D != NULL) && (*D != '\0')) {
      gtk_widget_set_sensitive(lookup_widget(widget, "okRegExWizard"), TRUE);
      gtk_entry_set_text(output, D);
      g_free(D);
    } else {
      gtk_widget_set_sensitive(lookup_widget(widget, "okRegExWizard"), FALSE);
      gtk_entry_set_text(output, "");
    }

    g_free(strArray);
}

gchar *makeInterimRegex(GtkComboBox *type, GtkEntry *entry, GtkComboBox *repeatc, gint position)
{
    gint iType = gtk_combo_box_get_active (type);
    const gchar *pEntry = gtk_entry_get_text(entry);
    gint iRepeat = gtk_combo_box_get_active (repeatc);
    
    return makeInterimRegex2(GTK_WIDGET(type), iType, pEntry, iRepeat, position);
}

gchar *makeInterimRegex2(GtkWidget *widget, gint type, const gchar *pEntry, gint repeatc, gint position)
{
    gchar *letters = NULL;
    gchar *retRegex = NULL;
    gchar letter[miniBuf];
    gchar prepend[miniBuf];
    gchar append[miniBuf];
    gchar repeat[miniBuf];
    gchar preBrkt[miniBuf];
    gchar postBrkt[miniBuf];
    gchar *a[9];
    gchar *tmpStr;
    gint i=0;
    gboolean convertInternal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget, "convertRegex")));
    
    memset(letter, 0x00, miniBuf);
    memset(prepend, 0x00, miniBuf);
    memset(append, 0x00, miniBuf);
    memset(repeat, 0x00, miniBuf);
    memset(preBrkt, 0x00, miniBuf);
    memset(postBrkt, 0x00, miniBuf);

    /* Set body */
    switch (type) {
        case REGWIZ_ANY_CHAR:
            g_strlcpy (letter, ".", miniBuf);
            break;
            
        case REGWIZ_THE_CHAR:
            if (*pEntry == '\0') {
                return NULL;
            }
            tmpStr = convertRegex(pEntry, convertInternal);
            g_strlcpy (letter, tmpStr, miniBuf);
            g_free(tmpStr);
            break;
            
        case REGWIZ_ANY_ONE_CHAR:
            if (*pEntry == '\0') {
                return NULL;
            }
            tmpStr = convertRegex2(pEntry, convertInternal);
            letters = g_strconcat("[", tmpStr, "]", NULL);
            g_free(tmpStr);
            break;
            
        case REGWIZ_ANY_CHAR_EXCEPT:
            if (*pEntry == '\0') {
                return NULL;
            }
            tmpStr = convertRegex2(pEntry, convertInternal);
            letters = g_strconcat("[^", tmpStr, "]", NULL);
            g_free(tmpStr);
            break;
            
        case REGWIZ_THE_PHRASE:
            if (*pEntry == '\0') {
                return NULL;
            }
            tmpStr = convertRegex(pEntry, convertInternal);
            letters = g_strconcat("(", tmpStr, ")", NULL);
            g_free(tmpStr);
            break;
            
        case REGWIZ_SPACE:
            g_strlcpy (letter, "\\s", miniBuf);
            break;
            
        case REGWIZ_ANY_NUMERIC:
            g_strlcpy (letter, "\\d", miniBuf);
            break;
            
        case REGWIZ_ANY_TEXT:
            g_strlcpy (letter, "\\w", miniBuf);
            break;
            
        case REGWIZ_DONT_KNOW:
        default:
            return NULL; /* This is just an empty string */
    }

    /* Set prefix/affix symbol */
    if (position == REGWIZ_START_TYPE) {
        g_strlcpy(prepend, "^", miniBuf);
    }
    if (position == REGWIZ_END_TYPE) {
        g_strlcpy(append, "$", miniBuf);
    }

    /* Set repeat symbol */
    switch (repeatc) {
        case REGWIZ_REPEAT_ONE_PLUS:
            g_strlcpy(repeat, "+", miniBuf);
            break;
        case REGWIZ_REPEAT_ZERO_PLUS:
            g_strlcpy(repeat, "*", miniBuf);
            break;
        case REGWIZ_REPEAT_ZERO_ONE:
            g_strlcpy(repeat, "?", miniBuf);
            break;
        default:
            /* Do nothing */
            break;
    }

    if ((*append != '\0') || (*prepend != '\0') || (*repeat != '\0')) {
        g_strlcpy(preBrkt, "(", miniBuf);
        g_strlcpy(postBrkt, ")", miniBuf);        
    }
    if (*prepend != '\0') {
        a[i++] = prepend;
    }
    if (*preBrkt != '\0') {
        a[i++] = preBrkt;
    }
    if (*letter != '\0') {
        a[i++] = letter;
    }
    if (letters != NULL) {
        a[i++] = letters;
    }
    if (*repeat != '\0') {
        a[i++] = repeat;
    }
    if (*postBrkt != '\0') {
        a[i++] = postBrkt;
    }
    if (*append != '\0') {
        a[i++] = append;
    }
    a[i++] = NULL;
    //    do {
    //    } while (i <= 5);
    
    retRegex = g_strjoinv("", a);

    if (letters != NULL) {
        g_free(letters);
    }

    return retRegex;
}


gchar *convertRegex(const gchar *input, gboolean change)
{
    gchar *outStr, *pOutStr;
    gchar *pInput = (gchar *)input;

    if (change == FALSE) {
        return (g_strdup(input));
    }

    outStr = g_malloc((2 * strlen(input)) + 1);
    pOutStr = outStr;
    do {
        switch (*pInput) {
            case '[':
            case '\\':
            case '^':
            case '$':
            case '.':
            case '|':
            case '?':
            case '*':
            case '+':
            case '(':
            case ')':
                (*outStr) = '\\';
                outStr++;
                break;
            default:
                break;
        }
        (*outStr) = *pInput;
        outStr ++;
        pInput ++;
    } while ((*pInput) != '\0');

    (*outStr) = '\0'; /* terminate the string */
    
    return pOutStr;
}

gchar *convertRegex2(const gchar *input, gboolean change)
{
    gchar *outStr, *pOutStr;
    gchar *pInput = (gchar *)input;
    gboolean incRightBrkt = FALSE;
    gboolean incCaret = FALSE;
    gboolean incMinus = FALSE;    

    outStr = g_malloc(strlen(input));
    pOutStr = outStr;
    do {
        switch (*pInput) {
            case ']':
                incRightBrkt = TRUE;
                break;
            case '^':
                incCaret = TRUE;
                break;
            case '-':
                incMinus = TRUE;
                break;
            default:
                (*outStr) = *pInput;
                outStr ++;
                break;
        }
        pInput ++;
    } while ((*pInput) != '\0');

    /* Deal with end specials */
    if (incCaret) {
        (*outStr) = '^';
        outStr ++;
    }
    if (incMinus) {
        (*outStr) = '-';
        outStr ++;
    }
    (*outStr) = '\0'; /* terminate the string */
    
    /* Deal with start specials */    
    if (incRightBrkt) {
        outStr = g_strconcat("]", pOutStr, NULL);
        g_free(pOutStr);
        pOutStr = outStr;
    }
    
    return pOutStr;
}

void
appendTableRow(GtkWidget *widget, gint num, ...) // gchar *entries[])
{
    gint i;
    va_list ap; /* define 'ap' as a pointer to the input strings */ 
    gint Inc=0;
    va_start(ap, num);
    GtkTreeIter iter;
    gchar *readString;
    GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(widget)));
 
    gtk_list_store_append (GTK_LIST_STORE(store), &iter);

    /* Loop through input strings, adding them to the current iter */
    do
    {
      gtk_list_store_set (store, &iter,
                          Inc, va_arg(ap, gchar *),
                          -1);
    } while (++Inc < num);
    va_end(ap);
}

gchar *resultsToCsvString(GtkWidget *widget)
{
  gchar *retString = NULL;
  gchar **retStringArray;
  GtkTreeIter iter;
  GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  GtkTreeModel *model = gtk_tree_view_get_model(treeview);
  gint lineCount = gtk_tree_model_iter_n_children (model, NULL);
  gint i = 0;
  gchar **tmpData;
  tmpData = malloc(7 * sizeof(gchar *));
  memset(tmpData, 0x00, sizeof(tmpData));

  g_assert(model != NULL);
  g_assert(treeview != NULL);

  /* Count number of lines in output window */
  if (lineCount <= 0) {
      return NULL;
  }
  retStringArray = malloc((2 + lineCount) * sizeof(gchar *));
  
  gtk_tree_model_get_iter_first (model, &iter);
  retStringArray[i++] = g_strdup("Name,Location,Size,Type,Modified");

  tmpData[5] = NULL;
  do {
      gtk_tree_model_get (model, &iter,
                          FILENAME_COLUMN, &tmpData[0],
                          LOCATION_COLUMN, &tmpData[1],
                          SIZE_COLUMN, &tmpData[2],
                          TYPE_COLUMN, &tmpData[3],
                          MODIFIED_COLUMN, &tmpData[4],
                          -1);
      
      retStringArray[i++] = g_strjoinv(",", tmpData);
      g_free (tmpData[0]);
      g_free (tmpData[1]);
      g_free (tmpData[2]);
      g_free (tmpData[3]);
      g_free (tmpData[4]);
  } while (gtk_tree_model_iter_next(model, &iter));
  retStringArray[i++] = NULL;

  retString = g_strjoinv("\n", retStringArray);
  g_strfreev(retStringArray);

  return retString;
}

void saveResults(GtkWidget *widget)
{
  GError *error = NULL;
  gchar *filename;
  GtkWidget *warnMsg;
  GtkWidget *errMsg;
  GtkWidget *dialog = create_saveFileDialog();
  GKeyFile *keyString = g_object_get_data(G_OBJECT(mainWindowApp), MASTER_OPTIONS_DATA);
  gchar *saveData = resultsToCsvString(widget);

  if (saveData == NULL) {
      errMsg = gtk_message_dialog_new(GTK_WINDOW(dialog),
                                      (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                      GTK_MESSAGE_INFO,
                                      GTK_BUTTONS_OK,
                                      "No results to save!");
      gtk_dialog_run(GTK_DIALOG(errMsg));
      gtk_widget_destroy(errMsg);
      return;
  }

  /* Set defaults, or get saved values*/
  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir());
  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "results.csv");
  realizeFileDialog(dialog, keyString, "saveResults", "saveFileDialog");
  
  while (1) {
    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
      if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
        warnMsg = gtk_message_dialog_new(GTK_WINDOW(dialog),
                                         (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                         GTK_MESSAGE_WARNING,
                                         GTK_BUTTONS_OK_CANCEL,
                                         "A file named \"%s\" already exists. Are you sure you want to overwrite it?",
                                         filename);
        if (gtk_dialog_run(GTK_DIALOG(warnMsg)) != GTK_RESPONSE_OK) {
          gtk_widget_destroy(warnMsg);
          g_free(filename);
          continue;
        }
        gtk_widget_destroy(warnMsg);
      }
      if (g_file_set_contents2 (filename, saveData, -1, &error)) {
        unrealizeFileDialog(dialog, keyString, "saveResults", "saveFileDialog");
        g_free(filename);
        break;
      } else {
        errMsg = gtk_message_dialog_new(GTK_WINDOW(dialog),
                                        (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                        GTK_MESSAGE_ERROR,
                                        GTK_BUTTONS_OK,
                                        error->message);
        gtk_dialog_run(GTK_DIALOG(errMsg));
        gtk_widget_destroy(errMsg);
        g_free(filename);
        g_error_free(error);
        error = NULL;
        continue;
      }
    } else {
      break;
    }
  }
  gtk_widget_destroy (dialog);
  g_free(saveData);
  return;
}


gchar *getFullFileName(GtkTreeView *treeView, gint columnNumber)
{
  GtkTreeSelection *selection = gtk_tree_view_get_selection(treeView); /* get selection */
  gchar *myString;
  GtkTreeIter iter;
  GtkTreeModel *model;

  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    gtk_tree_model_get (model, &iter,  columnNumber, &myString, -1);
    return myString;
  }
  return NULL;
}

void deleteFile(GtkWidget *widget)
{
  GtkTreeView *treeView = GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(widget), "treeview1"));
  gchar *fullFileName;
  GtkTreeSelection *selection = gtk_tree_view_get_selection(treeView); /* get selection */
  GtkTreeIter child_iter, iter;
  GtkWidget *dialog;
  GtkTreeModel *model = gtk_tree_view_get_model(treeView);
  GKeyFile *keyString = getGKeyFile(widget);
  GtkListStore *store;

  /* Get the line currently pointed at */
  if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
    dialog = gtk_message_dialog_new (GTK_WINDOW(lookup_widget(widget, "window1")),
                                     (GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
                                     GTK_MESSAGE_INFO,
                                     GTK_BUTTONS_OK,
                                     "Cannot delete file as name was not selected.",
                                     NULL);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy(dialog);
    return;
  }

  /* Get underlying model from the sort model */
  gtk_tree_model_get (model, &iter,  FULL_FILENAME_COLUMN, &fullFileName, -1);
  gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(model), &child_iter, &iter);
  store = GTK_LIST_STORE(gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(model)));

  /* Prompt (or don't) and delete file */
  if (fullFileName != NULL) {
    if (g_key_file_has_key(keyString, "configuration", "configPromptDelete", NULL) &&
        (!g_key_file_get_boolean(keyString, "configuration", "configPromptDelete", NULL))) {
      g_remove (fullFileName);
      gtk_list_store_remove(store, &child_iter);
    } else {
      dialog = gtk_message_dialog_new (GTK_WINDOW(lookup_widget(widget, "window1")),
                                       (GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
                                       GTK_MESSAGE_WARNING,
                                       GTK_BUTTONS_OK_CANCEL,
                                       "Are you sure you want to delete:\n\t'%s'",
                                       fullFileName);
      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
        g_remove (fullFileName);
        gtk_list_store_remove(store, &child_iter);
      }
      gtk_widget_destroy (dialog);
    }
    g_free(fullFileName);
  }
}

void copyFile(GtkWidget *widget)
{
  GtkTreeView *treeView = GTK_TREE_VIEW(lookup_widget(widget, "treeview1"));
  gchar *fullFileName = getFullFileName(treeView, FULL_FILENAME_COLUMN);
  GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
  GtkWidget *dialog;

  if (fullFileName != NULL) {
    gtk_clipboard_set_text (clipboard, fullFileName, -1);
    g_free(fullFileName);
  } else {
    dialog = gtk_message_dialog_new (GTK_WINDOW(lookup_widget(widget, "window1")),
                                     (GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL),
                                     GTK_MESSAGE_INFO,
                                     GTK_BUTTONS_OK,
                                     "Cannot copy full file name as name was not selected.",
                                     NULL);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy(dialog);
  }  
}
               

/* Wrapper functions for older Gtk versions */
gboolean g_file_get_contents2 (const gchar *filename,
                               gchar **contents,
                               gsize *length,
                               GError **error)
{
  gboolean retVal;
  gchar *retContents;
  
  retVal = g_file_get_contents(filename, contents, length, error);

  /* Remove unknown ASCII */
  if (retVal) {
    retContents = g_strescape(*contents, "\"\\\t\n\b\f\r");
    g_free(*contents);
    *contents = retContents;
    *length = strlen(retContents);
  }

  return retVal;
}


/* Wrapper functions for older Gtk versions */
gboolean g_file_set_contents2 (const gchar *filename,
                               const gchar *contents,
                               gssize length,
                               GError **error)
{
  FILE *fileSD;
  gssize wrote_size;
  gboolean retVal = TRUE;

  if (length == -1) {
    length = strlen(contents);
    if (length < 0) {
      set_internal_error(error, errno);
      return FALSE;
    }
  }
  
  fileSD = g_fopen(filename, "w");
  if (fileSD == NULL) {
    set_internal_error(error, errno);
    return FALSE;
  }
  
  wrote_size = fwrite(contents, 1, length, fileSD);

  if (length != wrote_size) {
    set_internal_error(error, errno);
    retVal = FALSE;
  }

  fclose(fileSD);
  
  return retVal;
}


void set_internal_error(GError **error, const gint err_no)
{
  GQuark domain = g_quark_from_string(PACKAGE "Error");
  GFileError  gerrno = g_file_error_from_errno (err_no);
  gchar *gstrerr = strerror(err_no);

  g_assert (gstrerr != NULL);
  
  if (error != NULL) {
    g_assert (*error == NULL);
    *error = g_error_new_literal(domain, gerrno, gstrerr);
  }
}

gchar *comboBoxReadCleanFolderName(GtkComboBox *combobox)
{
  gchar *retStr = NULL;
  gchar *tmpStr[3];
  gchar **test, **test2;
  tmpStr[0] = gtk_combo_box_get_active_text(combobox);

  /* Test that it is a proper filename */
  if (g_file_test (tmpStr[0], G_FILE_TEST_IS_DIR)) {
    if (g_path_is_absolute (tmpStr[0])) {
      retStr = g_filename_display_name(tmpStr[0]);
    } else {
      tmpStr[2] = g_filename_display_name(tmpStr[0]);
      if (tmpStr[2] != NULL) {
        retStr = g_build_path(G_DIR_SEPARATOR_S, g_get_current_dir(), tmpStr[2], NULL);
        g_free(tmpStr[2]);
      }
    }
  }
  if (retStr == NULL) {
    return tmpStr[0]; /* Failed! */
  }
  g_free(tmpStr[0]);
  
  /* Split directory into parts */
  test = g_strsplit (retStr, G_DIR_SEPARATOR_S, -1);
  if (test == NULL) {
    g_print("%s Error! Unable to split %s with %c\n", __func__, retStr, G_DIR_SEPARATOR_S);
    return retStr;
  }
  g_assert(test != NULL);
  
  /* Clean up any of the extra path separators */
  test2 = strvdup(test);
  g_strfreev(test);
  if (test2 == NULL){
    g_print("%s Error! Unable to duplicate string vector\n", __func__);
    return retStr;
  }

  /* Recreate the cleaned up path name */
  tmpStr[1] = g_strjoinv (G_DIR_SEPARATOR_S, test2);  
  g_strfreev(test2);

  return tmpStr[1];
}


gchar **strvdup(gchar **strv)
{
  gchar **retVal;
  gint i = 0, j = 0;

  g_assert(strv != NULL);
  
  retVal = malloc(sizeof(gchar *) * (1 + g_strv_length(strv)));

  retVal[j++] = g_strdup(strv[i++]); /* Always copy first element */
  while (strv[i] != NULL) {
    if (*strv[i] != '\0') {
      retVal[j++] = g_strdup(strv[i]);
    }
    i++;
  }
  retVal[j] = NULL; /* NULL terminate */

  return retVal;
}

void realize_configDialog (GtkWidget *widget)
{
  GKeyFile *keyString = getGKeyFile(mainWindowApp);
  GtkFileChooser *chooser;
  gchar *fullPath;

  /* Restore global settings */
  realizeToggle (widget, keyString, "configuration", "configForceSingle");
  realizeToggle (widget, keyString, "configuration", "configExtendedRegex");
  realizeToggle (widget, keyString, "configuration", "configPromptSave");
  realizeToggle (widget, keyString, "configuration", "configPromptDelete");
  realizeToggle (widget, keyString, "configuration", "configMatchBinary");
  realizeToggle (widget, keyString, "configuration", "autoAdjustColumnWidth");

  /* Restore store results settings */
  realizeCombo (widget, keyString, "configuration", "configResultFormat");
  realizeString (widget, keyString, "configuration", "configResultEOL");
  realizeString (widget, keyString, "configuration", "configResultEOF");
  
  /* Restore store results settings */
  realizeString (widget, keyString, "configuration", "configFileLocation");

  /* Restore system call settings */
  realizeFileButton (widget, keyString, "configuration", "configTextEditor");
  realizeString (widget, keyString, "configuration", "configTextEditorAttributes");
  realizeFileButton (widget, keyString, "configuration", "configFileExplorer");
  realizeString (widget, keyString, "configuration", "configFileExplorerAttributes");
  realizeFileButton (widget, keyString, "configuration", "configWebBrowser");
  realizeString (widget, keyString, "configuration", "configWebBrowserAttributes");

  fullPath = g_object_get_data(G_OBJECT(mainWindowApp), "configFileSaveLocation");
  if (fullPath != NULL) {
    gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "configFileLocation")), fullPath);
  } else {
    gtk_entry_set_text(GTK_ENTRY(lookup_widget(widget, "configFileLocation")), "[Config file does not yet exist!]");
  }  
}

void unrealize_configDialog (GtkWidget *widget)
{
  GKeyFile *keyString = getGKeyFile(mainWindowApp);
  GtkFileChooser *chooser;

  /* Restore global settings */
  unrealizeToggle (widget, keyString, "configuration", "configForceSingle");
  unrealizeToggle (widget, keyString, "configuration", "configExtendedRegex");
  unrealizeToggle (widget, keyString, "configuration", "configPromptSave");
  unrealizeToggle (widget, keyString, "configuration", "configPromptDelete");
  unrealizeToggle (widget, keyString, "configuration", "configMatchBinary");
  unrealizeToggle (widget, keyString, "configuration", "autoAdjustColumnWidth");

  /* Restore store results settings */
  unrealizeCombo (widget, keyString, "configuration", "configResultFormat");
  unrealizeString (widget, keyString, "configuration", "configResultEOL");
  unrealizeString (widget, keyString, "configuration", "configResultEOF");
  
  /* Restore store results settings */
  unrealizeString (widget, keyString, "configuration", "configFileLocation");

  /* Restore system call settings */
  unrealizeFileButton (widget, keyString, "configuration", "configTextEditor");
  unrealizeString (widget, keyString, "configuration", "configTextEditorAttributes");
  unrealizeFileButton (widget, keyString, "configuration", "configFileExplorer");
  unrealizeString (widget, keyString, "configuration", "configFileExplorerAttributes");
  unrealizeFileButton (widget, keyString, "configuration", "configWebBrowser");
  unrealizeString (widget, keyString, "configuration", "configWebBrowserAttributes");

}

gchar *replaceAttributeString(gchar *rawString, const gchar *replacement)
{
  GString *s1 = g_string_new (rawString);
  gchar *pRawString = rawString;
  gchar *ptrAttribute;
  gssize len = strlen(rawString);
  gint pos;

  ptrAttribute = g_strstr_len (pRawString, len, "%");
  pos = (ptrAttribute - pRawString);
  if (ptrAttribute != NULL) {
      switch (*(ptrAttribute + 1)) {
	  case 'f': /* Filename */
	  case 'd': /* Directory */
	      g_string_erase(s1, pos, 2);
	      g_string_insert(s1, pos, replacement);
	      break;
      }
  } else {
      if (rawString[len - 1] != ' ') {
	  g_string_append(s1, " ");
      }
      g_string_insert(s1, pos, replacement);
  }

  if (rawString[0] != ' ') {
      g_string_prepend(s1, " ");
  }

  return g_string_free (s1, FALSE);  
}


gpointer findExecutables(gpointer args)
{
  GObject *dialog = G_OBJECT(args);
  GtkProgressBar *pbar = GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(dialog), "progressbar3"));
  GtkWidget *OkButton = lookup_widget(GTK_WIDGET(dialog), "okbutton5");
  gchar **filenameList;
  volatile gboolean *cancelThread = g_object_get_data(G_OBJECT(dialog), "cancelThread");
  gint retVal = 0;
  gchar pbarText[6];
  gdouble pbarVal = 0;
  gint i, j;
  gchar **fList;
  gchar *tmpFilename;
  
  gdk_threads_enter ();
  filenameList = g_object_get_data(G_OBJECT(dialog), "returnFilenames");
  gtk_progress_bar_set_fraction(pbar, 0);
  gtk_progress_bar_set_text(pbar, "0%");
  
  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);
  gdk_threads_leave ();
  
  /* Disable all unused buttons */
  for (i=0; i<EXE_LIST_LEN; i++) { 

    if (*cancelThread != FALSE) {
      retVal = -1;
      break;
    }
    fList = (gchar **)exeList[i];
    j = 0;
    filenameList[i] = NULL;
    while ((filenameList[i] == NULL) &&
           ((fList[j] != NULL) && (*cancelThread == FALSE))) {
      gdk_threads_enter();
      pbarVal += MIN_STEP_VAL;
      g_sprintf(pbarText, "%.0lf%%", (pbarVal * 100));
      gtk_progress_bar_set_fraction(pbar, pbarVal);
      gtk_progress_bar_set_text(pbar, pbarText);
      gdk_threads_leave();
      tmpFilename = g_find_program_in_path(fList[j]);
      if (tmpFilename != NULL) {
        filenameList[i] = tmpFilename;
      }
      j++;
      usleep(SPIN_BAR_USLEEP_TIME);
    }

    pbarVal = ((i+1) * (1/3));
    g_sprintf(pbarText, "%.0lf%%", (pbarVal * 100));
    gdk_threads_enter ();
    gtk_progress_bar_set_fraction(pbar, pbarVal);
    gtk_progress_bar_set_text(pbar, pbarText);
    gdk_threads_leave ();
    usleep(SPIN_BAR_USLEEP_TIME);
  }
  
  /* All done! */
  gdk_threads_enter ();
  gtk_progress_bar_set_fraction(pbar, 1);
  gtk_progress_bar_set_text(pbar, "Done!");
  gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, TRUE);
  gdk_threads_leave ();
  if (retVal != 0) {
    gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
  }

  g_thread_exit(GINT_TO_POINTER(retVal));
}


void spawnNewSearchmonkey(void)
{
  gchar *searchmonkeyExe = g_object_get_data(G_OBJECT(mainWindowApp), "argvPointer");
  g_spawn_command_line_async (searchmonkeyExe, NULL); /* Assume it worked */  
}

void importCriteria(GtkWidget *widget) 
{
  GtkWidget *import = create_importCriteria();
  GList *allCriteria;
  GtkWidget *dropDownBox;
  gchar *filename;
  gchar **splitResult;
  gchar *contents;
  gsize length;
  gint i;
  gchar *orString;
  GError *error = NULL;
  GtkWidget *warnDialog;
  
  while (gtk_dialog_run(GTK_DIALOG(import)) == GTK_RESPONSE_OK) {
    filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(lookup_widget(import, "filechooserwidgetImport")));
    if (filename != NULL) {
      /* Choose the drop down box based on radio selection */
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(import, "fileNameRadioImport")))) {
        dropDownBox = lookup_widget(widget, "fileName");
      } else { /* Assume containing text import */
        dropDownBox = lookup_widget(widget, "containingText");      
      }
      
      if (g_file_get_contents (filename, &contents, &length, &error)) {
        splitResult = g_strsplit (contents, "\n", -1); /* Split completely */
        if (splitResult != NULL) {
          if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(import, "singleImportRadio")))) {
            orString = g_strjoinv("|", splitResult);
            if (orString[strlen(orString) - 1] == '|') {
              orString[strlen(orString) - 1] = '\0';
            }
            addUniqueRow(dropDownBox, orString);
            g_free(orString);
          } else { /* multiImportRadio */
            i = 0;
            while (splitResult[i] != NULL) {
              addUniqueRow(dropDownBox, splitResult[i]);
              i++;
            }
          }
          g_strfreev(splitResult);
          break;
        } else { /* !splitResult */
          warnDialog = gtk_message_dialog_new(GTK_WINDOW(import),
                                              (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                              GTK_MESSAGE_ERROR,
                                              GTK_BUTTONS_OK,
                                              "File was empty/invalid.");
          gtk_dialog_run(GTK_DIALOG(warnDialog));
          gtk_widget_destroy(warnDialog);
        }
        
      } else { /* !g_file_get_contents */
        warnDialog = gtk_message_dialog_new(GTK_WINDOW(import),
                                            (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                            GTK_MESSAGE_ERROR,
                                            GTK_BUTTONS_OK,
                                            error->message);
        gtk_dialog_run(GTK_DIALOG(warnDialog));
        g_error_free(error);
        error = NULL;
        gtk_widget_destroy(warnDialog);
      }
    } else { /* !filename */
      warnDialog = gtk_message_dialog_new(GTK_WINDOW(import),
                                          (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                          GTK_MESSAGE_ERROR,
                                          GTK_BUTTONS_OK,
                                          "Error! No valid file selected.");
      gtk_dialog_run(GTK_DIALOG(warnDialog));
      gtk_widget_destroy(warnDialog);
    }
  }
  gtk_widget_destroy(import);
}

void exportCriteria(GtkWidget *widget) 
{
  GtkWidget *export = create_exportCriteria();
  gchar *filename;
  GtkWidget *dropDownBox;
  GtkTreeIter iter;
  GtkTreeModel *model;
  GString *gstr = NULL;
  gchar *outputString;
  gchar *readString;
  GError *error;
  GtkWidget *warnDialog;

  while (gtk_dialog_run(GTK_DIALOG(export)) == GTK_RESPONSE_OK) {
    filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(lookup_widget(export, "filechooserwidgetExport")));
    if (filename != NULL) {
      /* Choose the drop down box based on radio selection */
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(export, "fileNameRadioExport")))) {
        dropDownBox = lookup_widget(widget, "fileName");
      } else { /* Assume containing text export */
        dropDownBox = lookup_widget(widget, "containingText");      
      }
      gstr = g_string_new ("");
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(export, "singleExportRadio")))) {
        if ( gtk_combo_box_get_active_iter(GTK_COMBO_BOX(dropDownBox), &iter)) {
          model = gtk_combo_box_get_model(GTK_COMBO_BOX(dropDownBox));
          gtk_tree_model_get (model, &iter,
                              0, &readString,
                              -1);
          if (readString != NULL) {  
            g_string_append(gstr, readString);
            g_string_append(gstr, "\n");
            g_free(readString);
          }
        } else { /* !gtk_combo_box_get_active_iter */
          warnDialog = gtk_message_dialog_new(GTK_WINDOW(export),
                                              (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                              GTK_MESSAGE_ERROR,
                                              GTK_BUTTONS_OK,
                                              "Error! You must select at least one entry in order to save drop down list.");
          gtk_dialog_run(GTK_DIALOG(warnDialog));
          gtk_widget_destroy(warnDialog);
        }
      } else { /* multiExportRadio */
        model = gtk_combo_box_get_model(GTK_COMBO_BOX(dropDownBox));
        if (gtk_tree_model_get_iter_first(model, &iter)) {
          do {
            gtk_tree_model_get (model, &iter,
                                0, &readString,
                                -1);
            if (readString != NULL) {  
              g_string_append(gstr, readString);
              g_string_append(gstr, "\n");
              g_free(readString);
            }
            /* Save each line to an open text file */
          } while (gtk_tree_model_iter_next(model, &iter));
        } else { /* !gtk_tree_model_get_iter_first */
          warnDialog = gtk_message_dialog_new(GTK_WINDOW(export),
                                              (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                              GTK_MESSAGE_ERROR,
                                              GTK_BUTTONS_OK,
                                              "Error! Unable to find first combo entry. Is drop down list blank?");
          gtk_dialog_run(GTK_DIALOG(warnDialog));
          gtk_widget_destroy(warnDialog);
        }
      }

      /* Save the text file to */
      if (gstr != NULL)
        outputString = g_string_free(gstr, FALSE);
      if (g_file_set_contents2(filename, outputString, -1, &error)) {
        g_free(outputString);
        g_free(filename);
        break; /* My work here is done! */
      } else {
        warnDialog = gtk_message_dialog_new(GTK_WINDOW(export),
                                            (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                            GTK_MESSAGE_ERROR,
                                            GTK_BUTTONS_OK,
                                            error->message);
        g_error_free(error);
        error = NULL;
        gtk_dialog_run(GTK_DIALOG(warnDialog));
        gtk_widget_destroy(warnDialog);        
      }
      
      g_free(outputString);
      g_free(filename);
    } else { /* !filename */
      warnDialog = gtk_message_dialog_new(GTK_WINDOW(export),
                                          (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                          GTK_MESSAGE_ERROR,
                                          GTK_BUTTONS_OK,
                                          "Error! No valid file selected.");
      gtk_dialog_run(GTK_DIALOG(warnDialog));
      gtk_widget_destroy(warnDialog);
    }
  }
  gtk_widget_destroy(export);  
}

/*
 * Equivalent to mkdir -p on linux
 */
gboolean mkFullDir(gchar *folderName, gint mode)
{
  gchar *partialFolderName, *pPartialFolderName;
  gchar **folderParts;
  gint i = 0;
  
  /* Completely split folderName into parts */
  folderParts = g_strsplit_set(folderName, G_DIR_SEPARATOR_S, -1);
  partialFolderName = g_strdup(folderName);
  pPartialFolderName = partialFolderName;

  while (folderParts[i] != NULL) {
    pPartialFolderName = g_stpcpy(pPartialFolderName, folderParts[i]);
    pPartialFolderName = g_stpcpy(pPartialFolderName, G_DIR_SEPARATOR_S);
    
    if (!g_file_test (partialFolderName, G_FILE_TEST_IS_DIR)) {
      g_mkdir(partialFolderName, mode);
    }
    i++;
  }
}
